@@ -34,36 +34,72 @@ After::
3434
3535 trio_asyncio.run(async_main, *args)
3636
37- Equivalently, wrap your main loop in a :func: `trio_asyncio.open_loop ` call ::
37+ Equivalently, wrap your main loop (or any other code that needs to talk to
38+ asyncio) in a :func: `trio_asyncio.open_loop ` call ::
3839
40+ import trio
3941 import trio_asyncio
4042
41- async def async_main (*args):
43+ async def async_main_wrapper (*args):
4244 async with trio_asyncio.open_loop() as loop:
43- pass # async main code goes here
45+ assert loop == asyncio.get_event_loop()
46+ await async_main(*args)
47+
48+ trio.run(async_main_wrapper, *args)
49+
50+ Within the ``async with `` block, an asyncio mainloop is active.
51+
52+ As this code demonstrates, you don't need to pass the ``loop `` argument
53+ around, as :func: `asyncio.get_event_loop ` will retrieve it when you're in
54+ the loop's context.
55+
56+ .. note ::
57+
58+ Don't do both. The following code **will not work **::
59+
60+ import trio
61+ import trio_asyncio
4462
45- Within the ``async with `` block, the asyncio mainloop is active. You don't
46- need to pass the ``loop `` argument around, as
47- :func: `asyncio.get_event_loop ` will do the right thing.
63+ async def async_main_wrapper(*args):
64+ async with trio_asyncio.open_loop() as loop:
65+ await async_main(*args)
66+
67+ trio_asyncio.run(async_main_wrapper, *args)
4868
4969.. autofunction :: trio_asyncio.open_loop
5070
5171.. autofunction :: trio_asyncio.run
5272
73+ .. note ::
74+
75+ The ``async with open_loop() `` way of running ``trio_asyncio `` is
76+ intended to transparently allow a library to use ``asyncio `` code,
77+ supported by a "local" asyncio loop, without affecting the rest of your
78+ Trio program.
79+
80+ However, currently this doesn't work because Trio does not yet support
81+ ``contextvars ``. Progress on this limitation is tracked in `this issue
82+ on github <https://github.com/python-trio/trio-asyncio/issues/9> `_.
83+
5384Stopping
5485--------
5586
5687The asyncio mainloop will be stopped automatically when the code within
57- ``async with open_loop() `` exits. Trio-asyncio will process all outstanding
58- callbacks and terminate. As in asyncio, callbacks which are added during
59- this step will be ignored.
88+ ``async with open_loop() `` / `` trio_asyncion.run() `` exits. Trio-asyncio
89+ will process all outstanding callbacks and terminate. As in asyncio,
90+ callbacks which are added during this step will be ignored.
6091
6192You cannot restart the loop, nor would you want to.
6293
63- Asyncio main loop
64- +++++++++++++++++
94+ Asyncio main loop.
95+ ++++++++++++++++++
96+
97+ Short answer: don't.
98+
99+ .. _native-loop :
65100
66- Well …
101+ Native Mode
102+ -----------
67103
68104What you really want to do is to use a Trio main loop, and run your asyncio
69105code in its context. In other words, you should transform this code::
@@ -75,35 +111,38 @@ code in its context. In other words, you should transform this code::
75111to this::
76112
77113 async def trio_main():
78- async with trio_asyncio.open_loop() as loop:
79- await loop.run_asyncio(async_main)
114+ await loop.run_asyncio(async_main)
80115
81116 def main():
82- trio.run(trio_main)
83-
84- You don't need to pass around the ``loop `` argument since trio remembers it
85- in its task structure: ``asyncio.get_event_loop() `` always works while
86- your program is executing an ``async with open_loop(): `` block.
117+ trio_asyncio.run(trio_main)
87118
88- There is no Trio equivalent to ``loop.run_forever() ``. The loop terminates
89- when you leave the ``async with `` block; it cannot be halted or restarted.
119+ Beside this, no changes to your code are required.
90120
91- This mode is called an "async loop" or "asynchronous loop" because it is
92- started from an async (Trio) context.
93-
94- Compatibility mode
121+ Compatibility Mode
95122------------------
96123
97124You still can do things "the asyncio way": the to-be-replaced code from the
98- previous section still works. However, behind the scenes
99- a separate thread executes the Trio main loop. It runs in lock-step with
100- the thread that calls ``loop.run_forever() `` or
101- ``loop.run_until_complete(coro) ``. Signals etc. get
102- delegated if possible (except for [SIGCHLD ]_). Thus, there should be no
103- concurrency issues.
125+ :ref: `previous section <native-loop >`
126+ still works – or at least it attempts to work::
104127
105- Caveat: you may still experience problems, particularly if your code (or
106- a library you're calling) does not expect to run in a different thread.
128+ import asyncio
129+ import trio_asyncio
130+ asyncio.set_event_loop_policy(trio_asyncio.TrioPolicy())
131+
132+ def main():
133+ loop = asyncio.get_event_loop()
134+ loop.run_until_complete(async_main())
135+
136+ .. warning ::
137+
138+ tl;dr: Don't use Compatibility Mode in production code.
139+
140+ However, this is only possible because this mode starts a separate thread
141+ which executes the asyncio main
142+ loop. It runs in lock-step with the code that calls ``loop.run_forever() ``
143+ or ``loop.run_until_complete(coro) ``. Signals etc. get
144+ delegated if possible (except for [SIGCHLD ]_). Thus, while there should be no
145+ concurrency issues, you may still experience hard-to-debug problems.
107146
108147.. [SIGCHLD ] Python requires you to register SIGCHLD handlers in the main
109148 thread, but doesn't run them at all when waiting for another thread.
@@ -116,20 +155,23 @@ a library you're calling) does not expect to run in a different thread.
116155with another call to ``loop.run_forever() `` or ``loop.run_until_complete(coro) ``,
117156just as with a regular asyncio loop.
118157
119- This mode is called a "sync loop" or "synchronous loop" because it is
120- started and used from a traditional synchronous Python context .
158+ If you use a compatibility- mode loop in a separate thread, you * must * stop and close it
159+ before terminating that thread. Otherwise your thread will leak resources .
121160
122- If you use a sync loop in a separate thread, you *must * stop and close it
123- before terminating the thread. Otherwise your thread will leak resources.
161+ In a multi-threaded program, globally setting the event loop policy may not
162+ be a good idea. If you want to run trio-asyncio in a separate thread, you
163+ might get away with using ``TrioPolicy().new_event_loop() `` to create a new
164+ event loop – but a far better idea is to use native mode.
165+
166+ .. note ::
124167
125- .. warning ::
126168 Compatibility mode has been added to verify that various test suites,
127- most notably the one from asyncio itself, continue to work. In a
169+ most notably the tests from asyncio itself, continue to work. In a
128170 real-world program with a long-running asyncio mainloop, you *really *
129- want to use a :ref: `Trio mainloop < trio -loop >` instead.
171+ want to use a :ref: `native-mode main loop < native -loop >` instead.
130172
131- The authors reserve the right to not fix compatibility mode bugs if that
132- would negatively impact trio-asyncio's core functions .
173+ The authors reserve the right to not fix compatibility mode bugs, or
174+ even to remove compatibility mode entirely .
133175
134176.. autoclass :: trio_asyncio.sync.SyncTrioEventLoop
135177
0 commit comments