##// END OF EJS Templates
doc, remove appveyor 3.4
Matthias Bussonnier -
Show More
@@ -1,95 +1,96 b''
1 1 """
2 2 Async helper function that are invalid syntax on Python 3.5 and below.
3 3
4 4 Known limitation and possible improvement.
5 5
6 6 Top level code that contain a return statement (instead of, or in addition to
7 7 await) will be detected as requiring being wrapped in async calls. This should
8 8 be prevented as early return will not work.
9 9 """
10 10
11 11
12 12 import ast
13 13 import sys
14 14 import inspect
15 15 from textwrap import dedent, indent
16 16 from types import CodeType
17 17
18 18
19 19 def _asyncio_runner(coro):
20 20 """
21 21 Handler for asyncio autoawait
22 22 """
23 23 import asyncio
24 24
25 25 return asyncio.get_event_loop().run_until_complete(coro)
26 26
27 27
28 28 def _curio_runner(coroutine):
29 29 """
30 30 handler for curio autoawait
31 31 """
32 32 import curio
33 33
34 34 return curio.run(coroutine)
35 35
36 36
37 37 if sys.version_info > (3, 5):
38 38 # nose refuses to avoid this file and async def is invalidsyntax
39 39 s = dedent(
40 40 '''
41 41 def _trio_runner(function):
42 42 import trio
43 43 async def loc(coro):
44 44 """
45 45 We need the dummy no-op async def to protect from
46 46 trio's internal. See https://github.com/python-trio/trio/issues/89
47 47 """
48 48 return await coro
49 49 return trio.run(loc, function)
50 50 '''
51 51 )
52 52 exec(s, globals(), locals())
53 53
54 54
55 55 def _asyncify(code: str) -> str:
56 56 """wrap code in async def definition.
57 57
58 58 And setup a bit of context to run it later.
59 59 """
60 60 res = dedent(
61 61 """
62 62 async def __wrapper__():
63 63 try:
64 64 {usercode}
65 65 finally:
66 66 locals()
67 67 """
68 68 ).format(usercode=indent(code, " " * 8)[8:])
69 69 return res
70 70
71 71
72 72 def _should_be_async(cell: str) -> bool:
73 73 """Detect if a block of code need to be wrapped in an `async def`
74 74
75 75 Attempt to parse the block of code, it it compile we're fine.
76 76 Otherwise we wrap if and try to compile.
77 77
78 78 If it works, assume it should be async. Otherwise Return False.
79 79
80 80 Not handled yet: If the block of code has a return statement as the top
81 81 level, it will be seen as async. This is a know limitation.
82 82 """
83 83
84 84 try:
85 85 # we can't limit ourself to ast.parse, as it __accepts__ to parse on
86 86 # 3.7+, but just does not _compile_
87 87 compile(cell, "<>", "exec")
88 88 return False
89 89 except SyntaxError:
90 90 try:
91 91 ast.parse(_asyncify(cell))
92 # TODO verify ast has not "top level" return or yield.
92 93 except SyntaxError:
93 94 return False
94 95 return True
95 96 return False
1 NO CONTENT: modified file
@@ -1,31 +1,27 b''
1 1 build: false
2 2 matrix:
3 3 fast_finish: true # immediately finish build once one of the jobs fails.
4 4
5 5 environment:
6 6 matrix:
7 7 - PYTHON: "C:\\Python36"
8 8 PYTHON_VERSION: "3.6.x"
9 9 PYTHON_ARCH: "32"
10 10
11 - PYTHON: "C:\\Python34-x64"
12 PYTHON_VERSION: "3.4.x"
13 PYTHON_ARCH: "64"
14
15 11 - PYTHON: "C:\\Python36-x64"
16 12 PYTHON_VERSION: "3.6.x"
17 13 PYTHON_ARCH: "64"
18 14
19 15 init:
20 16 - "ECHO %PYTHON% %PYTHON_VERSION% %PYTHON_ARCH%"
21 17
22 18 install:
23 19 - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
24 20 - "%CMD_IN_ENV% python -m pip install --upgrade setuptools pip"
25 21 - "%CMD_IN_ENV% pip install nose coverage"
26 22 - "%CMD_IN_ENV% pip install .[test]"
27 23 - "%CMD_IN_ENV% mkdir results"
28 24 - "%CMD_IN_ENV% cd results"
29 25 test_script:
30 26 - "%CMD_IN_ENV% iptest --coverage xml"
31 27
@@ -1,185 +1,193 b''
1 1 .. _autoawait:
2 2
3 3 Asynchronous in REPL: Autoawait
4 4 ===============================
5 5
6 Starting with IPython 6.0, and when user Python 3.6 and above, IPython offer the
7 ability to run asynchronous code from the REPL. constructs which are
6 Starting with IPython 7.0, and when user Python 3.6 and above, IPython offer the
7 ability to run asynchronous code from the REPL. Constructs which are
8 8 :exc:`SyntaxError` s in the Python REPL can be used seamlessly in IPython.
9 9
10 When a supported libray is used, IPython will automatically `await` Futures
11 and Coroutines in the REPL. This will happen if an :ref:`await <await>` (or `async`) is
10 The example given here are for terminal IPython, running async code in a
11 notebook interface or any other frontend using the Jupyter protocol will need to
12 use a newer version of IPykernel. The details of how async code runs in
13 IPykernel will differ between IPython, IPykernel and their versions.
14
15 When a supported library is used, IPython will automatically `await` Futures
16 and Coroutines in the REPL. This will happen if an :ref:`await <await>` is
12 17 use at top level scope, or if any structure valid only in `async def
13 18 <https://docs.python.org/3/reference/compound_stmts.html#async-def>`_ function
14 19 context are present. For example, the following being a syntax error in the
15 20 Python REPL::
16 21
17 22 Python 3.6.0
18 23 [GCC 4.2.1]
19 24 Type "help", "copyright", "credits" or "license" for more information.
20 25 >>> import aiohttp
21 26 >>> result = aiohttp.get('https://api.github.com')
22 27 >>> response = await result
23 28 File "<stdin>", line 1
24 29 response = await result
25 30 ^
26 31 SyntaxError: invalid syntax
27 32
28 33 Should behave as expected in the IPython REPL::
29 34
30 35 Python 3.6.0
31 36 Type 'copyright', 'credits' or 'license' for more information
32 IPython 6.0.0.dev -- An enhanced Interactive Python. Type '?' for help.
37 IPython 7.0.0 -- An enhanced Interactive Python. Type '?' for help.
33 38
34 39 In [1]: import aiohttp
35 40 ...: result = aiohttp.get('https://api.github.com')
36 41
37 42 In [2]: response = await result
38 43 <pause for a few 100s ms>
39 44
40 45 In [3]: await response.json()
41 46 Out[3]:
42 47 {'authorizations_url': 'https://api.github.com/authorizations',
43 48 'code_search_url': 'https://api.github.com/search/code?q={query}...',
44 49 ...
45 50 }
46 51
47 52
48 53 You can use the ``c.InteractiveShell.autoawait`` configuration option and set it
49 54 to :any:`False` to deactivate automatic wrapping of asynchronous code. You can also
50 55 use the :magic:`%autoawait` magic to toggle the behavior at runtime::
51 56
52 57 In [1]: %autoawait False
53 58
54 59 In [2]: %autoawait
55 60 IPython autoawait is `Off`, and set to use `IPython.core.interactiveshell._asyncio_runner`
56 61
57 62
58 63
59 64 By default IPython will assume integration with Python's provided
60 65 :mod:`asyncio`, but integration with other libraries is provided. In particular
61 we provide experimental integration with the ``curio`` and ``trio`` library.
66 we provide experimental integration with the ``curio`` and ``trio`` library, the
67 later one being necessary if you require the ability to do nested call of
68 IPython's ``embed()`` functionality.
62 69
63 70 You can switch current integration by using the
64 71 ``c.InteractiveShell.loop_runner`` option or the ``autoawait <name
65 72 integration>`` magic.
66 73
67 74 For example::
68 75
69 76 In [1]: %autoawait trio
70 77
71 78 In [2]: import trio
72 79
73 80 In [3]: async def child(i):
74 81 ...: print(" child %s goes to sleep"%i)
75 82 ...: await trio.sleep(2)
76 83 ...: print(" child %s wakes up"%i)
77 84
78 85 In [4]: print('parent start')
79 86 ...: async with trio.open_nursery() as n:
80 87 ...: for i in range(5):
81 88 ...: n.spawn(child, i)
82 89 ...: print('parent end')
83 90 parent start
84 91 child 2 goes to sleep
85 92 child 0 goes to sleep
86 93 child 3 goes to sleep
87 94 child 1 goes to sleep
88 95 child 4 goes to sleep
89 96 <about 2 seconds pause>
90 97 child 2 wakes up
91 98 child 1 wakes up
92 99 child 0 wakes up
93 100 child 3 wakes up
94 101 child 4 wakes up
95 102 parent end
96 103
97 104
98 105 In the above example, ``async with`` at top level scope is a syntax error in
99 106 Python.
100 107
101 108 Using this mode can have unexpected consequences if used in interaction with
102 109 other features of IPython and various registered extensions. In particular if you
103 110 are a direct or indirect user of the AST transformers, these may not apply to
104 111 your code.
105 112
106 The default loop, or runner does not run in the background, so top level
107 asynchronous code must finish for the REPL to allow you to enter more code. As
108 with usual Python semantic, the awaitables are started only when awaited for the
109 first time. That is to say, in first example, no network request is done between
110 ``In[1]`` and ``In[2]``.
113 When Using command line IPython, the default loop (or runner) does not process
114 in the background, so top level asynchronous code must finish for the REPL to
115 allow you to enter more code. As with usual Python semantic, the awaitables are
116 started only when awaited for the first time. That is to say, in first example,
117 no network request is done between ``In[1]`` and ``In[2]``.
111 118
112 119
113 120 Internals
114 121 =========
115 122
116 As running asynchronous code is not supported in interactive REPL as of Python
117 3.6 we have to rely to a number of complex workaround to allow this to happen.
118 It is interesting to understand how this works in order to understand potential
123 As running asynchronous code is not supported in interactive REPL (as of Python
124 3.7) we have to rely to a number of complex workaround to allow this to happen.
125 It is interesting to understand how this works in order to comprehend potential
119 126 bugs, or provide a custom runner.
120 127
121 128 Among the many approaches that are at our disposition, we find only one that
122 suited out need. Under the hood we :ct the code object from a async-def function
123 and run it in global namesapace after modifying the ``__code__`` object.::
129 suited out need. Under the hood we use the code object from a async-def function
130 and run it in global namespace after modifying it to not create a new
131 ``locals()`` scope::
124 132
125 133 async def inner_async():
126 134 locals().update(**global_namespace)
127 135 #
128 136 # here is user code
129 137 #
130 138 return last_user_statement
131 139 codeobj = modify(inner_async.__code__)
132 140 coroutine = eval(codeobj, user_ns)
133 141 display(loop_runner(coroutine))
134 142
135 143
136 144
137 145 The first thing you'll notice is that unlike classical ``exec``, there is only
138 146 one name space. Second, user code runs in a function scope, and not a module
139 147 scope.
140 148
141 149 On top of the above there are significant modification to the AST of
142 150 ``function``, and ``loop_runner`` can be arbitrary complex. So there is a
143 151 significant overhead to this kind of code.
144 152
145 153 By default the generated coroutine function will be consumed by Asyncio's
146 154 ``loop_runner = asyncio.get_evenloop().run_until_complete()`` method. It is
147 155 though possible to provide your own.
148 156
149 157 A loop runner is a *synchronous* function responsible from running a coroutine
150 158 object.
151 159
152 160 The runner is responsible from ensuring that ``coroutine`` run to completion,
153 161 and should return the result of executing the coroutine. Let's write a
154 162 runner for ``trio`` that print a message when used as an exercise, ``trio`` is
155 163 special as it usually prefer to run a function object and make a coroutine by
156 164 itself, we can get around this limitation by wrapping it in an async-def without
157 165 parameters and passing this value to ``trio``::
158 166
159 167
160 168 In [1]: import trio
161 169 ...: from types import CoroutineType
162 170 ...:
163 171 ...: def trio_runner(coro:CoroutineType):
164 172 ...: print('running asynchronous code')
165 173 ...: async def corowrap(coro):
166 174 ...: return await coro
167 175 ...: return trio.run(corowrap, coro)
168 176
169 177 We can set it up by passing it to ``%autoawait``::
170 178
171 179 In [2]: %autoawait trio_runner
172 180
173 181 In [3]: async def async_hello(name):
174 182 ...: await trio.sleep(1)
175 183 ...: print(f'Hello {name} world !')
176 184 ...: await trio.sleep(1)
177 185
178 186 In [4]: await async_hello('async')
179 187 running asynchronous code
180 188 Hello async world !
181 189
182 190
183 191 Asynchronous programming in python (and in particular in the REPL) is still a
184 relatively young subject. Feel free to contribute improvements to this codebase
185 and give us feedback.
192 relatively young subject. We expect some code to not behave as you expect, so
193 feel free to contribute improvements to this codebase and give us feedback.
@@ -1,44 +1,72 b''
1 1 =====================
2 2 Development version
3 3 =====================
4 4
5 5 This document describes in-flight development work.
6 6
7 7 .. warning::
8 8
9 9 Please do not edit this file by hand (doing so will likely cause merge
10 10 conflicts for other Pull Requests). Instead, create a new file in the
11 11 `docs/source/whatsnew/pr` folder
12 12
13 13
14 14 Released .... ...., 2017
15 15
16 16
17 17 Need to be updated:
18 18
19 19 .. toctree::
20 20 :maxdepth: 2
21 21 :glob:
22 22
23 23 pr/*
24 24
25 25 IPython 6 feature a major improvement in the completion machinery which is now
26 26 capable of completing non-executed code. It is also the first version of IPython
27 27 to stop compatibility with Python 2, which is still supported on the bugfix only
28 28 5.x branch. Read below to have a non-exhaustive list of new features.
29 29
30 30 Make sure you have pip > 9.0 before upgrading.
31 31 You should be able to update by using:
32 32
33 33 .. code::
34 34
35 35 pip install ipython --upgrade
36 36
37 37
38 Autowait: Asynchronous REPL
39 ===========================
40
41 Staring with IPython 7.0 and on Python 3.6+, IPython can automatically await
42 code at top level, you should not need to access an event loop or runner
43 yourself. To know more read the `autoawait`_ section of our docs, or try the
44 following code::
45
46 In [6]: from asyncio import sleep
47 ...: print('Going to sleep...')
48 ...: await sleep(3)
49 ...: print('Waking up')
50 Going to sleep...
51 Waking up
52
53 Asynchronous code in a Notebook interface or any other frontend using the
54 Jupyter Protocol will need further updates of the IPykernel package.
55
56
57 Change to Nested Embed
58 ======================
59
60 The introduction of the ability to run async code had ripple effect on the
61 ability to use nested IPython. You may need to install the ``trio`` library
62 (version 05 at the time of this writing) to
63 have this feature working.
64
65
38 66 .. DO NOT EDIT THIS LINE BEFORE RELEASE. FEATURE INSERTION POINT.
39 67
40 68
41 69 Backwards incompatible changes
42 70 ------------------------------
43 71
44 72 .. DO NOT EDIT THIS LINE BEFORE RELEASE. INCOMPAT INSERTION POINT.
General Comments 0
You need to be logged in to leave comments. Login now