Python Override Guidelines — PyGObject

# Python Override Guidelines

This document serves as a guide for developers creating new PyGObject overrides or modifying existing ones. This document is not intended as hard rules as there may always be pragmatic exceptions to what is listed here. It is also a good idea to study the Zen of Python by Tim Peters.

In general, overrides should be minimized and preference should always be placed on updating the underlying API to be more bindable, adding features to GI to support the requirement, or adding mechanical features to PyGObject which can apply generically to all overrides (bz#721226 and bz#640812).

If a GI feature or more bindable API for a library is in the works, it is a good idea to avoid the temptation to add temporary short term workarounds in overrides. The reason is this can creaste unnecessary conflicts when the bindable API becomes a reality (bz#707280).

- Minimize class overrides when possible.

  Reason: Class overrides incur a load time performance penalty because they require the classes GType and all of the Python method bindings to be created. See bz#705810

- Prefer monkey patching methods on repository classes over inheritance.

  Reason: Class overrides add an additional level to the method resolution order (mro) which has a performance penalty. Since overrides are designed for specific repository library APIs, monkey patching is reasonable because it is utilized in a controlled manner by the API designer (as opposed to monkey patching a third-party library which is more fragile).

- Avoid overriding `__init__` Reason: Sub-classing the overridden class then becomes challenging and has the potential to cause bugs (see bz#711487 and reasoning listed in https://wiki.gnome.org/Projects/PyGObject/InitializerDeprecations).

- Unbindable functions which take variadic arguments are generally ok to add Python implementations, but keep in mind the prior noted guidelines. A lot of times adding bindable versions of the functions to the underlying library which take a list is acceptable. For example: bz#706119. Another problem here is if an override is added, then later a bindable version of the API is added which takes a list, there is a good chance we have to live with the override forever which masks a working version implemented by GI.

- Avoid side effects beyond the intended repositories API in function/method overrides.

  Reason: This conflates the original API and adds a documentation burden on the override maintainer.

- Don’t change function signatures from the original API and don’t add default values.

  Reason: This turns into a documentation discrepancy between the libraries API and the Python version of the API. Default value work should focus on bug bz#558620, not cherry-picking individual Python functions and adding defaults.

- Avoid implicit side effects to the Python standard library (or anywhere).

  - Don’t modify or use sys.argv

    Reason: sys.argv should only be explicitly controlled by application developers. Otherwise it requires hacks to work around a module modifying or using the developers command line args which they rightfully own.

        saved_argv = sys.argv.copy()
        sys.argv = []
        from gi.repository import Gtk
        sys.argv = saved_argv

  - Never set Pythons default encoding.

    Reason: Read or watch Ned Batchelders “Pragmatic Unicode”

- For PyGTK compatibility APIs, add them to PyGTKCompat not overrides.

- Prefer adapter patterns over of inheritance and overrides.

  Reason: An adapter allows more flexibility and less dependency on overrides. It allows application developers to use the raw GI API without having to think about if a particular typelibs overrides have been installed or not.