File: asynchronous.rst

package info (click to toggle)
pygobject 3.54.5-7
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,864 kB
  • sloc: ansic: 40,281; python: 26,363; sh: 477; makefile: 81; xml: 35; cpp: 1
file content (171 lines) | stat: -rw-r--r-- 5,648 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
.. currentmodule:: gi.repository

Asynchronous Programming
========================

Asynchronous programming is an essential paradigm for handling tasks like I/O
operations, ensuring that your application remains responsive. GLib supports
asynchronous operations alongside its synchronous counterparts. These
asynchronous functions typically have a ``_async`` suffix and execute tasks in
the background, invoking a callback or returning a future-like object upon
completion or cancellation.

PyGObject offers two primary ways to implement asynchronous programming:

1. **Using Python's `asyncio` module** (available since PyGObject 3.50)
2. **Using callbacks** (the traditional approach)

Asynchronous Programming with ``asyncio``
-----------------------------------------

.. attention::
   Asyncio support for PyGObject is **experimental**. Feel free
   to explore its integration, but note that the API is subject to change as
   potential issues are addressed.

Overview of `asyncio` Integration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

PyGObject integrates seamlessly with Python's :mod:`asyncio` module by
providing:

1. An :mod:`asyncio` event loop implementation, enabling normal operation of
Python's asynchronous code.
2. Awaitable objects for Gio's asynchronous functions, allowing ``await``
syntax within Python coroutines.

Event Loop Integration
~~~~~~~~~~~~~~~~~~~~~~

To use the :mod:`asyncio` event loop with GLib, set up the GLib-based event loop
policy:

.. code-block:: python

    import asyncio
    from gi.events import GLibEventLoopPolicy

    # Set up the GLib event loop
    policy = GLibEventLoopPolicy()
    asyncio.set_event_loop_policy(policy)

Now, fetch the event loop and submit tasks:

.. code-block:: python

    loop = policy.get_event_loop()


    async def do_some_work():
        await asyncio.sleep(2)
        print("Done working!")


    task = loop.create_task(do_some_work())

.. note::

    Keep a reference to the tasks you create, as :mod:`asyncio` only
    maintains weak references to them.

Gio Asynchronous Function Integration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If the callback parameter of a Gio asynchronous function is omitted, PyGObject
automatically returns an awaitable object (similar to :class:`asyncio.Future`).
This allows you to use ``await`` and cancel operations from within a coroutine.

.. code-block:: python

    loop = policy.get_event_loop()


    async def list_files():
        f = Gio.file_new_for_path("/")
        for info in await f.enumerate_children_async(
            "standard::*", 0, GLib.PRIORITY_DEFAULT
        ):
            print(info.get_display_name())


    task = loop.create_task(list_files())

Example: Download Window with Async Feedback
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Here is a full example illustrating asynchronous programming with
:mod:`asyncio` for a download window with async feedback.

.. literalinclude:: download_asyncio.py
   :language: python

Key Considerations
~~~~~~~~~~~~~~~~~~

* Async tasks use :obj:`GLib.PRIORITY_DEFAULT`. For background tasks,
  consider using a lower priority to avoid affecting the responsiveness of
  your GTK UI. Please see the `PRIORITY_* GLib Constants
  <https://docs.gtk.org/glib/index.html#constants>` for other settings.
* Prefer starting your application using
  :obj:`Gio.Application` or :obj:`Gtk.Application` instead of
  :func:`asyncio.run`, which is incompatible.

Asynchronous Programming with Callbacks
---------------------------------------

The traditional callback approach is a robust alternative for asynchronous
programming in PyGObject. Consider this example of downloading a web page and
displaying its source in a text field. The operation can also be canceled
mid-execution.

.. literalinclude:: download_callback.py
   :language: python

Synchronous Comparison
~~~~~~~~~~~~~~~~~~~~~~

   Before exploring the asynchronous method, let’s review the simpler blocking
   approach:

.. code-block:: python

    file = Gio.File.new_for_uri(
        "https://developer.gnome.org/documentation/tutorials/beginners.html"
    )
    try:
        status, contents, etag_out = file.load_contents(None)
    except GLib.GError:
        print("Error!")
    else:
        print(contents)

Asynchronous Workflow with Callbacks
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

For asynchronous tasks, you’ll need:

1. A :class:`Gio.Cancellable` to allow task cancellation.
2. A :class:`Gio.AsyncReadyCallback` function to handle the result upon task
   completion.

The example setup includes:

* **Start Button**: The handler calls :meth:`Gio.File.load_contents_async`
  with a cancellable and a callback function.
* **Cancel Button**: The handler calls :meth:`Gio.Cancellable.cancel` to stop
  the operation.

Once the operation completes—whether successfully, due to an error, or because
it was canceled—the ``on_ready_callback()`` function is invoked. This callback
receives two arguments: the :class:`Gio.File` instance and a
:class:`Gio.AsyncResult` instance containing the operation's result.

To retrieve the result, call :meth:`Gio.File.load_contents_finish`. This method
behaves like :meth:`Gio.File.load_contents`, but since the operation has
already completed, it returns immediately without blocking.

After handling the result, call :meth:`Gio.Cancellable.reset` to prepare the
:class:`Gio.Cancellable` for reuse in future operations. This ensures that the
"Load" button can be clicked again to initiate another task. The application
enforces that only one operation is active at a time by disabling the "Load"
button during an ongoing task using :meth:`Gtk.Widget.set_sensitive`.