# Pycairo: the Python binding layer over the cairo graphics library

> Pycairo is the Python module that binds the cairo graphics library, with a cairo >= 1.15.10 requirement and Python 3.11 or newer. The bindings mirror the C API closely, translate error status into exceptions, ship type annotations, and are licensed LGPL-2.1-only or MPL-1.1 at your choice.

**pygobject/pycairo** — Python bindings for cairo

- Repository: https://github.com/pygobject/pycairo
- Website: https://pycairo.readthedocs.io
- Stars: 699 · Forks: 89
- Language: C
- License: NOASSERTION
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/pygobject-pycairo

## What Pycairo is, and what it is not

Pycairo is a Python module providing bindings for the cairo graphics library, and the README puts the design intent in plain terms. The bindings are designed to match the cairo C API as closely as possible, and to deviate only in cases which are clearly better implemented in a more Pythonic way. That sentence is the whole contract. You are not learning a new drawing model, you are learning cairo, and the differences you will notice are the places where Python already had a better idea. The README lists what the bindings give you: an object oriented interface to cairo, queries of the error status of objects translated into exceptions, a C API that can be used by other Python extensions, and a fully typed and documented API. The primary language of the repository is C, since the module wraps a C library, and the documentation lives at pycairo.readthedocs.io.

## Requirements, and the reason pip install is not the whole story

The dependency statement is short and has two halves. Pycairo depends on cairo >= 1.15.10, and it works with Python 3.11 and newer as well as with PyPy3. The pyproject.toml agrees, with requires-python set to >=3.11 and the project version at 1.29.3, which is one patch ahead of the most recent tagged release, v1.29.2. The install command itself is short.

```bash
pip install pycairo
```

But the README immediately qualifies it: installing Pycairo requires cairo including its headers, and points at the Getting Started page for more detail. That is the whole installation story in two sentences, and the second sentence is the one that matters. Pycairo is a compiled extension module, so the system needs a cairo development package as well as the runtime library, and on a machine without it the pip install will fail at the build step rather than at import time. This is a different experience from installing a pure Python package, and it is the single most common source of friction for people coming to Pycairo from, say, Pillow.

## A first drawing, in the shape the README gives

The README's example is a compact illustration of the object model, and it is worth reading closely because it shows the shape of the API rather than just one drawing. You create a surface with cairo.SVGSurface, which here takes a filename and a width and a height, you obtain a context from it, and you draw on the context. The surface is used as a context manager, so the file is written and closed when the block ends.

```python
import cairo

with cairo.SVGSurface("example.svg", 200, 200) as surface:
    context = cairo.Context(surface)
    x, y, x1, y1 = 0.1, 0.5, 0.4, 0.9
    x2, y2, x3, y3 = 0.6, 0.1, 0.9, 0.5
    context.scale(200, 200)
    context.set_line_width(0.04)
    context.move_to(x, y)
    context.curve_to(x1, y1, x2, y2, x3, y3)
    context.stroke()
    context.set_source_rgba(1, 0.2, 0.2, 0.6)
    context.set_line_width(0.02)
    context.move_to(x, y)
    context.line_to(x1, y1)
    context.move_to(x2, y2)
    context.line_to(x3, y3)
    context.stroke()
```

Two details set the coordinates. The context is scaled by 200, 200, so the coordinate values in the drawing code are fractions of the 200 by 200 surface, and the line widths are fractions too, which is how you get resolution independent output. And the drawing calls are the cairo calls: move_to, curve_to, line_to, stroke, set_source_rgba, set_line_width. Nothing here is a Pycairo invention.

## How the binding is built: Meson, and a C extension other packages can reuse

The build system is the visible part of the packaging story, and it has changed. The repository root carries meson.build, meson_options.txt and a pyproject.toml whose build backend is mesonpy, and there is a tool.meson-python.args section that sets the wheel option and disables the tests for a release build. The earlier setup.py style is gone. The extension source lives in the cairo/ directory, and the tests in tests/. The README also notes that Pycairo provides a C API that can be used by other Python extensions, which is a genuinely useful property if you are writing a Python extension of your own and want cairo drawing without reimplementing it. The dependency-groups in pyproject.toml show the development stack: pytest, a pinned mypy, flake8, coverage and meson-python, with Sphinx and the read the docs theme for the docs group, and pygame and numpy in a test-extras group.

## Typing is taken seriously, and the mypy config shows it

One of the four advertised features is a fully typed and documented API, and the mypy configuration in pyproject.toml is where you can see that this is enforced rather than aspirational. The settings disallow unimported any, any expressions, untyped calls and untyped definitions, and they warn on returning any, disallow implicit optional, and set strict_optional. Untyped definitions are still checked, and the only exception is a module override for setup, with the tests module allowed to be less strict on untyped definitions and any expressions. The examples, build and docs directories are excluded from the check. The consequence for a user is practical. Editors and type checkers will tell you what a cairo Context method expects, and if you are building a drawing library on top of Pycairo, the type information is there rather than something you have to reconstruct from the C header.

## The licence choice, and why it is dual

The README states that Pycairo, including this documentation, is licensed under LGPL-2.1-only OR MPL-1.1, and the pyproject.toml declares the same as the licence text, with COPYING-LGPL-2.1 and COPYING-MPL-1.1 both present in the repository root next to COPYING. The repository listing classifies the licence as other, a custom licence GitHub cannot categorise, which is what happens with a dual licence expressed through SPDX identifiers. The choice is yours, and it is worth understanding before you ship anything. The LGPL route is a weak copyleft on the library itself, and it is the licence to pick if you want to keep the licensing of your own application separate. The MPL route is file level copyleft, where modifications to the covered files must be published, and it is a better fit if you intend to contribute changes back to the project. Either way the obligation attaches to the library and its files, not to the output your program draws. Read the two licence texts yourself rather than taking this paragraph as legal advice.

## The limits, and what to use instead

Two boundaries are clear. The first is the dependency on a system cairo with headers, which rules out environments where you cannot install system packages, and it is why the pip install line on its own is misleading on locked down machines. The second is that Pycairo draws, it does not composite images. If your work is manipulating bitmaps, cropping, resizing and filtering, that is a different library, and the cairo route is the wrong one. Pycairo is for vector output, which is exactly what its SVG surface in the example is for, and the same object model has surfaces for other targets. The comparison to make is with a higher level binding built on the same library, if you want drawing without the cairo call sequence, and with a pure Python imaging library, if you want raster manipulation instead of vector output. Neither replaces the other, and picking the wrong one costs you a dependency on a C library for no benefit.

## Maintenance record and what the examples directory holds

The repository is not archived, and the last push was on 2026-10-11. The recent tags are v1.29.2 from 2026-10-04, v1.29.1 from 2026-08-07 and v1.29.0 from 2025-11-11, so the 1.29 line is where the work is happening, and the version in pyproject.toml is already at 1.29.3, one patch ahead of the newest tag. The examples directory is the fastest way to see the library in use: a cairo_snippets/ folder, a gtk/ folder, a pycairo_examples.ipynb notebook, and a pygame-demo.py, which shows the same drawing code driven from a game loop. The documentation build is configured through .readthedocs.yaml and the docs/ directory, and the CI configuration lives in .ci/ and .github/ with a codecov configuration for coverage reporting. The maintainer listed in pyproject.toml is Christoph Reiter, and the project is hosted under the pygobject organisation, which is the same group that maintains the GTK Python bindings.

## Conclusion

Adopt Pycairo if you need to draw vector graphics from Python and you want the cairo API rather than a higher level abstraction, since the bindings are designed to match the C API as closely as possible and only deviate where Python offers a better way. Do not adopt it if you cannot install the cairo library and its headers, because the module is a compiled binding and not a pure Python package. Before you start, check that the platform provides cairo 1.15.10 or newer and Python 3.11 or later, and decide which of the two licences you are relying on, LGPL-2.1-only or MPL-1.1, because the choice matters for how you distribute a linked application.

## FAQ

### What is Pycairo and what does it bind?

Pycairo is a Python module providing bindings for the cairo graphics library. The bindings are designed to match the cairo C API as closely as possible and deviate only where a more Pythonic implementation is clearly better.

### What do I need to install Pycairo?

The README says to run pip install pycairo, and immediately adds that installing Pycairo requires cairo including its headers, with the Getting Started page linked for details. It depends on cairo 1.15.10 or newer and works with Python 3.11 and later as well as PyPy3.

### Does Pycairo support type checking?

Yes. The README lists a fully typed and documented API among the features, and the mypy settings in pyproject.toml disallow untyped calls and definitions, warn on returning any and set strict_optional, with only the setup module and the tests given looser rules.

### What licence does Pycairo use?

The README and pyproject.toml both state LGPL-2.1-only OR MPL-1.1, and the repository carries COPYING-LGPL-2.1 and COPYING-MPL-1.1 alongside COPYING. You pick the licence that fits how you distribute your application.

### How is Pycairo built?

The build uses Meson through the mesonpy backend declared in pyproject.toml, with meson.build and meson_options.txt at the repository root and the extension sources in the cairo/ directory. The wheel option is set and the tests are disabled for a release build.

## Sources

- [Issues](https://github.com/pygobject/pycairo/issues)
- [Project website](https://pycairo.readthedocs.io)
- [pygobject/pycairo on GitHub](https://github.com/pygobject/pycairo)
- [README](https://github.com/pygobject/pycairo/blob/main/README.md)
- [Releases](https://github.com/pygobject/pycairo/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pygobject-pycairo
