After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 707196 - pygobject depends on unmaintained software
pygobject depends on unmaintained software
Status: RESOLVED FIXED
Product: pygobject
Classification: Bindings
Component: general
unspecified
Other Linux
: Normal normal
: ---
Assigned To: Nobody's working on this now (help wanted and appreciated)
Python bindings maintainers
Depends on:
Blocks:
 
 
Reported: 2013-08-31 20:20 UTC by shy
Modified: 2017-04-20 08:47 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Bump pycairo requirement to 1.11.1 (1.76 KB, patch)
2017-04-19 13:13 UTC, Christoph Reiter (lazka)
committed Details | Review

Description shy 2013-08-31 20:20:37 UTC
pygobject depends on unmaintained and outdated Python-Cairo binding(pycairo).
The current version of pycairo lacks many important features(RecordingSurface, ImageSurface.get_data(), cairo_region_t, etc.) that have been introduced to Cairo recently. This makes pygobject actually worse than pygtk.
Comment 1 Simon Feltman 2013-09-12 23:26:10 UTC
Should we move PyGObject to use cairocffi?
http://pythonhosted.org/cairocffi
Comment 2 Simon Feltman 2013-09-13 00:26:45 UTC
It looks like cairocffi is in Fedora 18 and 19:
https://bugzilla.redhat.com/show_bug.cgi?id=986715

But I could not find anything for debian/ubuntu. So this would need either a configure switch or runtime check for probably a great while.

./configure --with-cairo-cffi

or

import gi
gi.enable_cairo_cffi()

Furthermore, there is no C API for cairocffi which we would need to handle by some other means (implement a light C wrapper which interacts with cairocffi):
http://www.cairographics.org/documentation/pycairo/3/pycairo_c_api.html
Comment 3 Simon Feltman 2013-09-13 04:35:13 UTC
I fear cairocffi will be too slow and our efforts might be better spent trying to get an updated release of pycairo out. I think it would be a serious performance regression for anyone writing custom controls or viewports which rely on cairo drawing. I'm seeing about a 6 times slow down running the simple tutorial: http://cairographics.org/pycairo/tutorial/

pycairo:
$ time python3 example.py
real    0m0.034s
user    0m0.032s
sys     0m0.000s

caircffi:
$ time python3 example.py
real    0m0.211s
user    0m0.192s
sys     0m0.016s
Comment 4 Simon Sapin 2013-09-16 08:48:39 UTC
Hi. cairocffi developer here.

I don’t think Debian packages should stop you from using cairocffi. It’s pure Python itself and thus should be very easy to packages, and other than cairo and Python only requires CFFI, which itself is in Debian. (Though admittedly not in stable.)

As to the C API: what exactly does pygobject need? If you’re willing to make changes to pygobject to make this work, I think we can come up with a subset (or equivalent thereof) that can be safely implemented in cairocffi.

As to performance, you’re also measuring start up. cairocffi parses a bunch of C declarations on startup (which takes some constant amount of time), but after that performs fine in my experience. The following benchmark (largely taken from the tutorial but not measuring startup) gives a difference between cairocffi and pycairo that is within the measuring error margin.


#!/usr/bin/env python

import io
import math
import timeit
import cairocffi as cairo

def run():
    WIDTH, HEIGHT = 256, 256

    surface = cairo.ImageSurface (cairo.FORMAT_ARGB32, WIDTH, HEIGHT)
    ctx = cairo.Context (surface)

    ctx.scale (WIDTH, HEIGHT) # Normalizing the canvas

    pat = cairo.LinearGradient (0.0, 0.0, 0.0, 1.0)
    pat.add_color_stop_rgba (1, 0.7, 0, 0, 0.5) # First stop, 50% opacity
    pat.add_color_stop_rgba (0, 0.9, 0.7, 0.2, 1) # Last stop, 100% opacity

    ctx.rectangle (0, 0, 1, 1) # Rectangle(x0, y0, x1, y1)
    ctx.set_source (pat)
    ctx.fill ()

    ctx.translate (0.1, 0.1) # Changing the current transformation matrix

    ctx.move_to (0, 0)
    ctx.arc (0.2, 0.1, 0.1, -math.pi/2, 0) # Arc(cx, cy, radius, start_angle, stop_angle)
    ctx.line_to (0.5, 0.1) # Line to (x,y)
    ctx.curve_to (0.5, 0.2, 0.5, 0.4, 0.2, 0.8) # Curve(x1, y1, x2, y2, x3, y3)
    ctx.close_path ()

    ctx.set_source_rgb (0.3, 0.2, 0.5) # Solid color
    ctx.set_line_width (0.02)
    ctx.stroke ()

    file_obj = io.BytesIO() # Bypass the filesystem to avoid measuring IO.
    surface.write_to_png (file_obj) # Output to PNG


print(min(timeit.repeat(stmt=run, repeat=3, number=100)))
Comment 5 Simon Feltman 2013-09-19 03:38:07 UTC
Thanks for commenting. Startup is something we need to consider since OLPC uses PyGObject and we have done a lot of work optimizing startup times in general. I think these are fairly low end machines (latest gen seems to be single core 800mhz, 512mb ram) so even small performance differences on a desktop can have big effect.
https://bugzilla.gnome.org/showdependencytree.cgi?id=693243&hide_resolved=0

With startup out of the way as you've posted, it basically becomes IO bound which wouldn't be a fair test for custom control drawing or more importantly viewport/clutter drawing. So by removing the IO and context creation, we have a more representative test for what a widget "draw" signal might experience (just draw call marshaling differences). This starts to show a bigger performance difference.

But I might be splitting hairs with that, so perhaps what we need viewport frame rates of something more complex to make any kind of judgement.

In any event, I think pypy based technologies is a good direction and I'm not opposed to cairocffi integration generally speaking. We just need to work through performance and distribution issues and support it through an explicit switch app developers need to set. This is why I was attempting the approach of mimicking the Pycairo API inside of cairocffi as it is the least intrusive and most re-usable approach, but I don't think it can ever be complete.

In terms of what we need as an API, we have a foreign object marshaler specifically for cairo:
https://git.gnome.org/browse/pygobject/tree/gi/pygi-foreign-cairo.c
Comment 6 Simon Sapin 2013-09-19 08:47:50 UTC
I started discussing caching the results of parsing C declarations with the CFFI developers, to improve the startup time.

https://bitbucket.org/cffi/cffi/issue/26/

I think that pygi-foreign-cairo.c could work by using cairocffi’s ._pointer and ._from_pointer(). I would be much more confident in that than in a simulated C API.
Comment 7 Simon Sapin 2013-09-19 09:14:51 UTC
(To deal with CFFI not having a C API, I could add ._get_address() and ._from_address() on cairocffi wrappers that work with Python integers, which pygi-foreign-cairo.c could use by casting between C pointers and uintptr_t.)
Comment 8 Simon Feltman 2013-09-20 19:04:46 UTC
(In reply to comment #7)
> (To deal with CFFI not having a C API, I could add ._get_address() and
> ._from_address() on cairocffi wrappers that work with Python integers, which
> pygi-foreign-cairo.c could use by casting between C pointers and uintptr_t.)

That would be helpful, thanks!
Comment 9 Simon Sapin 2013-09-21 11:12:23 UTC
I see that that file binds on cairo.Path objects. cairocffi does not have Path objects but just converts paths to lists if tuples, and back. Do you think it is useful to have actual Path objects? What in PyGObject uses them? (Maybe what introspected API?)
Comment 10 Simon Sapin 2013-09-22 12:54:51 UTC
Actually I’m hesitant with having ._get_address() and ._from_address() in cairocffi itself, as they shouldn’t be used IMO unless one knows what they’re doing. There’re quite easy to do as well:

Getting the address: int(cairocffi.ffi.cast('uintptr_t', context._pointer))
Make a new wrapper: Context._from_pointer(cairocffi.ffi.cdata('cairo_t*', address))

Yes, this is a bit more verbose in C with CPython’s C API than in Python, but still doable and not terribly complex.
Comment 11 Patrick Welche 2013-12-14 19:39:43 UTC
I'm a little lost with all the cairo options. What is that pygobject gains from the pycairo dependency? What does cairo-gobject's cairo-1.0.gir provide (or lack)? What is lost if one compiles pygobject with --disable-cairo?
Comment 12 Simon Sapin 2013-12-15 01:54:15 UTC
Patrick: Roughly, PyGObject uses GObject introspection to automatically create Python bindings for GObject types and API. This doesn’t just work for cairo, because cairo does not use GObject but has an API based on pointers to plain C structs.

Therefore, we have bindings for Python such as pycairo (which is unfortunately unmaintained) and cairocffi (which I wrote as a replacement for pycairo.)

When cairo types are involved in a GObject API (for example gdk_cairo_create() returns a cairo_t*), PyGObject needs to know how to convert between C pointers to cairo structs and Python objects that wrap them. In current versions, there is code that compiles against pycairo’s C API in order to do that.

https://developer.gnome.org/gdk3/stable/gdk3-Cairo-Interaction.html#gdk-cairo-create
https://git.gnome.org/browse/pygobject/tree/gi/pygi-foreign-cairo.c

That code is (I suppose) what is disabled by --disable-cairo. With --disable-cairo, one can not use from Python any API that involves cairo structs.

This bug is about replacing that code with something that uses cairocffi instead of pycairo.
Comment 13 Patrick Welche 2013-12-15 12:33:47 UTC
Thank you for the explanation! Have you any thoughts on my second question:

> What does cairo-gobject's cairo-1.0.gir provide (or lack)?
Comment 14 Simon Sapin 2013-12-15 21:17:30 UTC
I have no idea what cairo-1.0.gir does or if it’s useful at all to PyGObject, maybe someone else can explain.
Comment 15 Simon Feltman 2013-12-16 10:04:46 UTC
(In reply to comment #11)
> What is lost if one compiles pygobject with --disable-cairo?

The lost functionality I know about would be custom drawing in both GTK+ and Clutter:
https://developer.gnome.org/gtk3/stable/GtkWidget.html#GtkWidget-draw
https://developer.gnome.org/clutter/stable/ClutterCanvas.html#ClutterCanvas-draw

I'm sure there is more, but if you aren't using doing custom widget/viewport drawing I'm sure you won't be missing much. Also note that the dependency is only needed for a seperate .so plugin to PyGObject (_gi_cairo.cpython-33m.so) and is  packaged separately in debian at least (python-gi-cairo).
Comment 16 Stuart Axon 2015-04-21 09:30:58 UTC
This ticket makes a lot of sense - I've just been porting some code to gtk3 and cairocffi.

The situation becomes even more confusing when you start to use 'pgi' as well as being compatible with cairocffi.   

'pgi' seems to be compatible with cairocffi, which is great - hopefully we can replace pygoject with it one day - unfortunately it doesn't support all bindings right now, so I need to support pygobject as well.

My code has to

1 - detect if using 'gi'
2 - if (1) convert between pycairo contexts and matrixes (for Gtk consumption) in many places.


In my ideal world, the pygobject guys would have a hackfest with the 'pgi' guys and get that up to speed and replace pygobject with it (being based on cffi it can work on pypy and other python implementations).

In the meantime getting support for cairocffi is sorely needed.
Comment 17 Stuart Axon 2016-09-17 14:31:16 UTC
> 
> I started discussing caching the results of parsing C declarations with the
> CFFI developers, to improve the startup time.
> 
> https://bitbucket.org/cffi/cffi/issue/26/

On that bug report it looks like they implemented this in CFFI 1.0:

"This is exactly what cffi 1.0 did with its new out-of-line mode (both for ABI and API level)."

Which looks like it could resolve the performance issues mentioned above.
Comment 18 Christoph Reiter (lazka) 2017-04-09 18:41:04 UTC
I've tried to move things forward for pycairo by doing an external release: https://lists.cairographics.org/archives/cairo/2017-April/027919.html
Comment 19 Christoph Reiter (lazka) 2017-04-18 21:31:01 UTC
Proposal to make pygobject depend on the fork:

https://mail.gnome.org/archives/desktop-devel-list/2017-April/msg00065.html

and for the release team:

https://mail.gnome.org/archives/release-team/2017-April/msg00048.html
Comment 20 Christoph Reiter (lazka) 2017-04-19 13:13:02 UTC
Created attachment 350067 [details] [review]
Bump pycairo requirement to 1.11.1

This requires pycairo from https://pycairo.readthedocs.io/en/latest/
(already updated/included in JHBuild)

For more info on the upstream change see:
    https://lists.cairographics.org/archives/cairo/2017-April/027919.html
    https://mail.gnome.org/archives/desktop-devel-list/2017-April/msg00065.html

This will enable cairo.Region support for all setups and Python versions as well
as make moving to Python 3 easier for applications since all APIs provided
under Python 2 are available there as well now.