PyGObject Migration Guide

pangocffi (and pangocairocffi) can be replaced by PyGObject, but there are several dependencies and API calls that need to be changed. The migration is worthwhile because PyGObject’s bindings are more complete than pangocffi’s, and it’s also maintained by more people. However there are a few drawbacks, like needing to replace cairocffi with Pycairo, which not everyone will be able to do.

pangocffi can continue to be used in the short term, but if it breaks in future versions of Python, cairocffi, and cffi, the project will have to be forked and fixed. In such an event, the LGPL 2.1 license must be respected.

This guide covers various details for switching to PyGObject, including:

  • Replacing system dependencies

  • Replacing Python packages

  • Replacing usages of cairocffi with Pycairo

  • Switching usages of pangocffi and pangocairocffi’s API to PyGObject

  • An example using PyGObject

Replacing System Dependencies

Which system dependencies to install will differ between Linux, macOS, Windows, etc.

To give some general guidance, here’s a before and after of the system dependencies specifically for Debian Trixie. Other systems might have different names for these dependencies.

Before the migration, pangocffi and pangocairocffi required these dependencies:

  • libcairo2

  • libpango-1.0-0

  • libpangocairo-1.0-0

To use Pango and PangoCairo with PyGObject, install these dependencies:

  • pkg-config

  • libcairo2-dev

  • libpango1.0-dev

  • libpangocairo-1.0-0

  • libgirepository-2.0-dev

The -dev suffix is important because these will include bindings that PyGObject will use to interface with Pango and PangoCairo.

If the libraries above don’t work, see Getting Started with Pycairo and Getting Started with PyGObject for guidance on which additional libraries to install. Note that PyGObject’s guide might include extra dependencies that are irrelevant for using Pango and Pangocairo.

Replacing Python Packages

Before the migration, these were the Python packages necessary:

pip install cairocffi
pip install pangocffi
pip install pangocairocffi

After the migration, these are now packages to install instead:

pip install pycairo
pip install pygobject
pip install pygobject-stubs # Optional for type-hinting

Switching from cairocffi to Pycairo

cairocffi does not appear to work with PyGObject, but Pycairo does. Therefore cairocffi must be replaced with Pycairo.

cairocffi is meant to be a drop-in replacement for Pycairo, so it should be a matter of changing the import in a lot of places from one package to another:

# Before
import cairocffi as cairo
from cairocffi import ImageSurface

# After
import cairo
from cairo import ImageSurface

Type hinting also changes subtly:

# Before
my_function(
    surface: cairo.surfaces.Surface,
    context: cairo.context.Context
) -> None

# After
my_function(
    surface: cairo.Surface,
    context: cairo.Context
) -> None

There are some inconsistencies between cairocffi and PyCairo, so make sure to review the code for errors. For example, in Pycairo, matrices cannot be copied using .copy():

# Before, cairocffi.Matrix has a copy method
new_matrix = old_matrix.copy()

# After, Pycairo requires an explicit initialisation of a new Matrix
new_matrix = cairo.Matrix(
    old_matrix.xx,
    old_matrix.yx,
    old_matrix.xy,
    old_matrix.yy,
    old_matrix.x0,
    old_matrix.y0,
)

Switching from pangocffi and pangocairocffi’s API to PyGObject

There are a few differences between pangocffi’s and PyGObject’s implementation of API methods. Below are a few examples:

Imports

pangocffi was imported with:

import pangocffi
import pangocairocffi

PyGObject instead requires:

import gi
gi.require_version("Pango", "1.0")
gi.require_version("PangoCairo", "1.0")
from gi.repository import Pango  # noqa: E402
from gi.repository import PangoCairo  # noqa: E402

Getters and setters

pangocffi uses the property decorator pattern for implementing getter and setter methods:

my_width = layout.width
layout.width = 123

PyGObject however uses plain get_x and set_x for getter and setter methods:

my_width = layout.get_width()
layout.set_width(123)

Using Layout Markup

pangocffi’s Layout object has a method called apply_markup(...). PyGObject calls the method set_markup(...) instead.

PyGObject API reference

There are likely many more differences not listed here. See the GNOME Python API for Pango and PangoCairo for more details on additional methods. For other GNOME libraries, see the GNOME Python API documentation.

Example

In case there are any installation issues, a functioning example has been created to use as a reference. The example runs in a in a Debian environment using Docker. This can be useful for troubleshooting any unexpected migration issues when switching from pangocffi to PyGObject.