##// END OF EJS Templates
Fix magic directive and role....
Matthias Bussonnier -
Show More
@@ -1,7 +1,9 b''
1 .. _extensions_autoreload:
1 .. _extensions_autoreload:
2
2
3 ==========
3 ==========
4 autoreload
4 autoreload
5 ==========
5 ==========
6
6
7 .. magic:: autoreload
8
7 .. automodule:: IPython.extensions.autoreload
9 .. automodule:: IPython.extensions.autoreload
@@ -1,316 +1,317 b''
1 .. _autoawait:
1 .. _autoawait:
2
2
3 Asynchronous in REPL: Autoawait
3 Asynchronous in REPL: Autoawait
4 ===============================
4 ===============================
5
5
6 .. note::
6 .. note::
7
7
8 This feature is experimental and behavior can change between python and
8 This feature is experimental and behavior can change between python and
9 IPython version without prior deprecation.
9 IPython version without prior deprecation.
10
10
11 Starting with IPython 7.0, and when user Python 3.6 and above, IPython offer the
11 Starting with IPython 7.0, and when user Python 3.6 and above, IPython offer the
12 ability to run asynchronous code from the REPL. Constructs which are
12 ability to run asynchronous code from the REPL. Constructs which are
13 :exc:`SyntaxError` s in the Python REPL can be used seamlessly in IPython.
13 :exc:`SyntaxError` s in the Python REPL can be used seamlessly in IPython.
14
14
15 The examples given here are for terminal IPython, running async code in a
15 The examples given here are for terminal IPython, running async code in a
16 notebook interface or any other frontend using the Jupyter protocol needs
16 notebook interface or any other frontend using the Jupyter protocol needs
17 IPykernel version 5.0 or above. The details of how async code runs in IPykernel
17 IPykernel version 5.0 or above. The details of how async code runs in IPykernel
18 will differ between IPython, IPykernel and their versions.
18 will differ between IPython, IPykernel and their versions.
19
19
20 When a supported library is used, IPython will automatically allow Futures and
20 When a supported library is used, IPython will automatically allow Futures and
21 Coroutines in the REPL to be ``await`` ed. This will happen if an :ref:`await
21 Coroutines in the REPL to be ``await`` ed. This will happen if an :ref:`await
22 <await>` (or any other async constructs like async-with, async-for) is use at
22 <await>` (or any other async constructs like async-with, async-for) is use at
23 top level scope, or if any structure valid only in `async def
23 top level scope, or if any structure valid only in `async def
24 <https://docs.python.org/3/reference/compound_stmts.html#async-def>`_ function
24 <https://docs.python.org/3/reference/compound_stmts.html#async-def>`_ function
25 context are present. For example, the following being a syntax error in the
25 context are present. For example, the following being a syntax error in the
26 Python REPL::
26 Python REPL::
27
27
28 Python 3.6.0
28 Python 3.6.0
29 [GCC 4.2.1]
29 [GCC 4.2.1]
30 Type "help", "copyright", "credits" or "license" for more information.
30 Type "help", "copyright", "credits" or "license" for more information.
31 >>> import aiohttp
31 >>> import aiohttp
32 >>> result = aiohttp.get('https://api.github.com')
32 >>> result = aiohttp.get('https://api.github.com')
33 >>> response = await result
33 >>> response = await result
34 File "<stdin>", line 1
34 File "<stdin>", line 1
35 response = await result
35 response = await result
36 ^
36 ^
37 SyntaxError: invalid syntax
37 SyntaxError: invalid syntax
38
38
39 Should behave as expected in the IPython REPL::
39 Should behave as expected in the IPython REPL::
40
40
41 Python 3.6.0
41 Python 3.6.0
42 Type 'copyright', 'credits' or 'license' for more information
42 Type 'copyright', 'credits' or 'license' for more information
43 IPython 7.0.0 -- An enhanced Interactive Python. Type '?' for help.
43 IPython 7.0.0 -- An enhanced Interactive Python. Type '?' for help.
44
44
45 In [1]: import aiohttp
45 In [1]: import aiohttp
46 ...: result = aiohttp.get('https://api.github.com')
46 ...: result = aiohttp.get('https://api.github.com')
47
47
48 In [2]: response = await result
48 In [2]: response = await result
49 <pause for a few 100s ms>
49 <pause for a few 100s ms>
50
50
51 In [3]: await response.json()
51 In [3]: await response.json()
52 Out[3]:
52 Out[3]:
53 {'authorizations_url': 'https://api.github.com/authorizations',
53 {'authorizations_url': 'https://api.github.com/authorizations',
54 'code_search_url': 'https://api.github.com/search/code?q={query}...',
54 'code_search_url': 'https://api.github.com/search/code?q={query}...',
55 ...
55 ...
56 }
56 }
57
57
58
58
59 You can use the ``c.InteractiveShell.autoawait`` configuration option and set it
59 You can use the ``c.InteractiveShell.autoawait`` configuration option and set it
60 to :any:`False` to deactivate automatic wrapping of asynchronous code. You can
60 to :any:`False` to deactivate automatic wrapping of asynchronous code. You can
61 also use the :magic:`%autoawait` magic to toggle the behavior at runtime::
61 also use the :magic:`autoawait` magic to toggle the behavior at runtime::
62
62
63 In [1]: %autoawait False
63 In [1]: %autoawait False
64
64
65 In [2]: %autoawait
65 In [2]: %autoawait
66 IPython autoawait is `Off`, and set to use `asyncio`
66 IPython autoawait is `Off`, and set to use `asyncio`
67
67
68
68
69
69
70 By default IPython will assume integration with Python's provided
70 By default IPython will assume integration with Python's provided
71 :mod:`asyncio`, but integration with other libraries is provided. In particular
71 :mod:`asyncio`, but integration with other libraries is provided. In particular
72 we provide experimental integration with the ``curio`` and ``trio`` library.
72 we provide experimental integration with the ``curio`` and ``trio`` library.
73
73
74 You can switch current integration by using the
74 You can switch current integration by using the
75 ``c.InteractiveShell.loop_runner`` option or the ``autoawait <name
75 ``c.InteractiveShell.loop_runner`` option or the ``autoawait <name
76 integration>`` magic.
76 integration>`` magic.
77
77
78 For example::
78 For example::
79
79
80 In [1]: %autoawait trio
80 In [1]: %autoawait trio
81
81
82 In [2]: import trio
82 In [2]: import trio
83
83
84 In [3]: async def child(i):
84 In [3]: async def child(i):
85 ...: print(" child %s goes to sleep"%i)
85 ...: print(" child %s goes to sleep"%i)
86 ...: await trio.sleep(2)
86 ...: await trio.sleep(2)
87 ...: print(" child %s wakes up"%i)
87 ...: print(" child %s wakes up"%i)
88
88
89 In [4]: print('parent start')
89 In [4]: print('parent start')
90 ...: async with trio.open_nursery() as n:
90 ...: async with trio.open_nursery() as n:
91 ...: for i in range(5):
91 ...: for i in range(5):
92 ...: n.spawn(child, i)
92 ...: n.spawn(child, i)
93 ...: print('parent end')
93 ...: print('parent end')
94 parent start
94 parent start
95 child 2 goes to sleep
95 child 2 goes to sleep
96 child 0 goes to sleep
96 child 0 goes to sleep
97 child 3 goes to sleep
97 child 3 goes to sleep
98 child 1 goes to sleep
98 child 1 goes to sleep
99 child 4 goes to sleep
99 child 4 goes to sleep
100 <about 2 seconds pause>
100 <about 2 seconds pause>
101 child 2 wakes up
101 child 2 wakes up
102 child 1 wakes up
102 child 1 wakes up
103 child 0 wakes up
103 child 0 wakes up
104 child 3 wakes up
104 child 3 wakes up
105 child 4 wakes up
105 child 4 wakes up
106 parent end
106 parent end
107
107
108
108
109 In the above example, ``async with`` at top level scope is a syntax error in
109 In the above example, ``async with`` at top level scope is a syntax error in
110 Python.
110 Python.
111
111
112 Using this mode can have unexpected consequences if used in interaction with
112 Using this mode can have unexpected consequences if used in interaction with
113 other features of IPython and various registered extensions. In particular if
113 other features of IPython and various registered extensions. In particular if
114 you are a direct or indirect user of the AST transformers, these may not apply
114 you are a direct or indirect user of the AST transformers, these may not apply
115 to your code.
115 to your code.
116
116
117 When using command line IPython, the default loop (or runner) does not process
117 When using command line IPython, the default loop (or runner) does not process
118 in the background, so top level asynchronous code must finish for the REPL to
118 in the background, so top level asynchronous code must finish for the REPL to
119 allow you to enter more code. As with usual Python semantic, the awaitables are
119 allow you to enter more code. As with usual Python semantic, the awaitables are
120 started only when awaited for the first time. That is to say, in first example,
120 started only when awaited for the first time. That is to say, in first example,
121 no network request is done between ``In[1]`` and ``In[2]``.
121 no network request is done between ``In[1]`` and ``In[2]``.
122
122
123
123
124 Effects on IPython.embed()
124 Effects on IPython.embed()
125 --------------------------
125 --------------------------
126
126
127 IPython core being asynchronous, the use of ``IPython.embed()`` will now require
127 IPython core being asynchronous, the use of ``IPython.embed()`` will now require
128 a loop to run. By default IPython will use a fake coroutine runner which should
128 a loop to run. By default IPython will use a fake coroutine runner which should
129 allow ``IPython.embed()`` to be nested. Though this will prevent usage of the
129 allow ``IPython.embed()`` to be nested. Though this will prevent usage of the
130 ``autoawait`` feature when using IPython embed.
130 :magic:`autoawait` feature when using IPython embed.
131
131
132 You can set explicitly a coroutine runner for ``embed()`` if you desire to run
132 You can set explicitly a coroutine runner for ``embed()`` if you desire to run
133 asynchronous code, the exact behavior is though undefined.
133 asynchronous code, the exact behavior is though undefined.
134
134
135 Effects on Magics
135 Effects on Magics
136 -----------------
136 -----------------
137
137
138 A couple of magics (``%%timeit``, ``%timeit``, ``%%time``, ``%%prun``) have not
138 A couple of magics (``%%timeit``, ``%timeit``, ``%%time``, ``%%prun``) have not
139 yet been updated to work with asynchronous code and will raise syntax errors
139 yet been updated to work with asynchronous code and will raise syntax errors
140 when trying to use top-level ``await``. We welcome any contribution to help fix
140 when trying to use top-level ``await``. We welcome any contribution to help fix
141 those, and extra cases we haven't caught yet. We hope for better support in Cor
141 those, and extra cases we haven't caught yet. We hope for better support in Cor
142 Python for top-level Async code.
142 Python for top-level Async code.
143
143
144 Internals
144 Internals
145 ---------
145 ---------
146
146
147 As running asynchronous code is not supported in interactive REPL (as of Python
147 As running asynchronous code is not supported in interactive REPL (as of Python
148 3.7) we have to rely to a number of complex workaround and heuristic to allow
148 3.7) we have to rely to a number of complex workaround and heuristic to allow
149 this to happen. It is interesting to understand how this works in order to
149 this to happen. It is interesting to understand how this works in order to
150 comprehend potential bugs, or provide a custom runner.
150 comprehend potential bugs, or provide a custom runner.
151
151
152 Among the many approaches that are at our disposition, we find only one that
152 Among the many approaches that are at our disposition, we find only one that
153 suited out need. Under the hood we use the code object from a async-def function
153 suited out need. Under the hood we use the code object from a async-def function
154 and run it in global namespace after modifying it to not create a new
154 and run it in global namespace after modifying it to not create a new
155 ``locals()`` scope::
155 ``locals()`` scope::
156
156
157 async def inner_async():
157 async def inner_async():
158 locals().update(**global_namespace)
158 locals().update(**global_namespace)
159 #
159 #
160 # here is user code
160 # here is user code
161 #
161 #
162 return last_user_statement
162 return last_user_statement
163 codeobj = modify(inner_async.__code__)
163 codeobj = modify(inner_async.__code__)
164 coroutine = eval(codeobj, user_ns)
164 coroutine = eval(codeobj, user_ns)
165 display(loop_runner(coroutine))
165 display(loop_runner(coroutine))
166
166
167
167
168
168
169 The first thing you'll notice is that unlike classical ``exec``, there is only
169 The first thing you'll notice is that unlike classical ``exec``, there is only
170 one namespace. Second, user code runs in a function scope, and not a module
170 one namespace. Second, user code runs in a function scope, and not a module
171 scope.
171 scope.
172
172
173 On top of the above there are significant modification to the AST of
173 On top of the above there are significant modification to the AST of
174 ``function``, and ``loop_runner`` can be arbitrary complex. So there is a
174 ``function``, and ``loop_runner`` can be arbitrary complex. So there is a
175 significant overhead to this kind of code.
175 significant overhead to this kind of code.
176
176
177 By default the generated coroutine function will be consumed by Asyncio's
177 By default the generated coroutine function will be consumed by Asyncio's
178 ``loop_runner = asyncio.get_evenloop().run_until_complete()`` method if
178 ``loop_runner = asyncio.get_evenloop().run_until_complete()`` method if
179 ``async`` mode is deemed necessary, otherwise the coroutine will just be
179 ``async`` mode is deemed necessary, otherwise the coroutine will just be
180 exhausted in a simple runner. It is though possible to change the default
180 exhausted in a simple runner. It is though possible to change the default
181 runner.
181 runner.
182
182
183 A loop runner is a *synchronous* function responsible from running a coroutine
183 A loop runner is a *synchronous* function responsible from running a coroutine
184 object.
184 object.
185
185
186 The runner is responsible from ensuring that ``coroutine`` run to completion,
186 The runner is responsible from ensuring that ``coroutine`` run to completion,
187 and should return the result of executing the coroutine. Let's write a
187 and should return the result of executing the coroutine. Let's write a
188 runner for ``trio`` that print a message when used as an exercise, ``trio`` is
188 runner for ``trio`` that print a message when used as an exercise, ``trio`` is
189 special as it usually prefer to run a function object and make a coroutine by
189 special as it usually prefer to run a function object and make a coroutine by
190 itself, we can get around this limitation by wrapping it in an async-def without
190 itself, we can get around this limitation by wrapping it in an async-def without
191 parameters and passing this value to ``trio``::
191 parameters and passing this value to ``trio``::
192
192
193
193
194 In [1]: import trio
194 In [1]: import trio
195 ...: from types import CoroutineType
195 ...: from types import CoroutineType
196 ...:
196 ...:
197 ...: def trio_runner(coro:CoroutineType):
197 ...: def trio_runner(coro:CoroutineType):
198 ...: print('running asynchronous code')
198 ...: print('running asynchronous code')
199 ...: async def corowrap(coro):
199 ...: async def corowrap(coro):
200 ...: return await coro
200 ...: return await coro
201 ...: return trio.run(corowrap, coro)
201 ...: return trio.run(corowrap, coro)
202
202
203 We can set it up by passing it to ``%autoawait``::
203 We can set it up by passing it to ``%autoawait``::
204
204
205 In [2]: %autoawait trio_runner
205 In [2]: %autoawait trio_runner
206
206
207 In [3]: async def async_hello(name):
207 In [3]: async def async_hello(name):
208 ...: await trio.sleep(1)
208 ...: await trio.sleep(1)
209 ...: print(f'Hello {name} world !')
209 ...: print(f'Hello {name} world !')
210 ...: await trio.sleep(1)
210 ...: await trio.sleep(1)
211
211
212 In [4]: await async_hello('async')
212 In [4]: await async_hello('async')
213 running asynchronous code
213 running asynchronous code
214 Hello async world !
214 Hello async world !
215
215
216
216
217 Asynchronous programming in python (and in particular in the REPL) is still a
217 Asynchronous programming in python (and in particular in the REPL) is still a
218 relatively young subject. We expect some code to not behave as you expect, so
218 relatively young subject. We expect some code to not behave as you expect, so
219 feel free to contribute improvements to this codebase and give us feedback.
219 feel free to contribute improvements to this codebase and give us feedback.
220
220
221 We invite you to thoroughly test this feature and report any unexpected behavior
221 We invite you to thoroughly test this feature and report any unexpected behavior
222 as well as propose any improvement.
222 as well as propose any improvement.
223
223
224 Using Autoawait in a notebook (IPykernel)
224 Using Autoawait in a notebook (IPykernel)
225 -----------------------------------------
225 -----------------------------------------
226
226
227 Update ipykernel to version 5.0 or greater::
227 Update ipykernel to version 5.0 or greater::
228
228
229 pip install ipykernel ipython --upgrade
229 pip install ipykernel ipython --upgrade
230 # or
230 # or
231 conda install ipykernel ipython --upgrade
231 conda install ipykernel ipython --upgrade
232
232
233 This should automatically enable ``autoawait`` integration. Unlike terminal
233 This should automatically enable :magic:`autoawait` integration. Unlike
234 IPython, all code runs on ``asyncio`` eventloop, so creating a loop by hand will
234 terminal IPython, all code runs on ``asyncio`` eventloop, so creating a loop by
235 not work, including with magics like ``%run`` or other frameworks that create
235 hand will not work, including with magics like :magic:`%run` or other
236 the eventloop themselves. In cases like these you can try to use projects like
236 frameworks that create the eventloop themselves. In cases like these you can
237 `nest_asyncio <https://github.com/erdewit/nest_asyncio>`_ and follow `this discussion
237 try to use projects like `nest_asyncio
238 <https://github.com/erdewit/nest_asyncio>`_ and follow `this discussion
238 <https://github.com/jupyter/notebook/issues/3397#issuecomment-419386811>`_
239 <https://github.com/jupyter/notebook/issues/3397#issuecomment-419386811>`_
239
240
240 Difference between terminal IPython and IPykernel
241 Difference between terminal IPython and IPykernel
241 -------------------------------------------------
242 -------------------------------------------------
242
243
243 The exact asynchronous code running behavior varies between Terminal IPython and
244 The exact asynchronous code running behavior varies between Terminal IPython and
244 IPykernel. The root cause of this behavior is due to IPykernel having a
245 IPykernel. The root cause of this behavior is due to IPykernel having a
245 *persistent* ``asyncio`` loop running, while Terminal IPython starts and stops a
246 *persistent* `asyncio` loop running, while Terminal IPython starts and stops a
246 loop for each code block. This can lead to surprising behavior in some case if
247 loop for each code block. This can lead to surprising behavior in some case if
247 you are used to manipulate asyncio loop yourself, see for example
248 you are used to manipulate asyncio loop yourself, see for example
248 :ghissue:`11303` for a longer discussion but here are some of the astonishing
249 :ghissue:`11303` for a longer discussion but here are some of the astonishing
249 cases.
250 cases.
250
251
251 This behavior is an implementation detail, and should not be relied upon. It can
252 This behavior is an implementation detail, and should not be relied upon. It can
252 change without warnings in future versions of IPython.
253 change without warnings in future versions of IPython.
253
254
254 In terminal IPython a loop is started for each code blocks only if there is top
255 In terminal IPython a loop is started for each code blocks only if there is top
255 level async code::
256 level async code::
256
257
257 $ ipython
258 $ ipython
258 In [1]: import asyncio
259 In [1]: import asyncio
259 ...: asyncio.get_event_loop()
260 ...: asyncio.get_event_loop()
260 Out[1]: <_UnixSelectorEventLoop running=False closed=False debug=False>
261 Out[1]: <_UnixSelectorEventLoop running=False closed=False debug=False>
261
262
262 In [2]:
263 In [2]:
263
264
264 In [2]: import asyncio
265 In [2]: import asyncio
265 ...: await asyncio.sleep(0)
266 ...: await asyncio.sleep(0)
266 ...: asyncio.get_event_loop()
267 ...: asyncio.get_event_loop()
267 Out[2]: <_UnixSelectorEventLoop running=True closed=False debug=False>
268 Out[2]: <_UnixSelectorEventLoop running=True closed=False debug=False>
268
269
269 See that ``running`` is ``True`` only in the case were we ``await sleep()``
270 See that ``running`` is ``True`` only in the case were we ``await sleep()``
270
271
271 In a Notebook, with ipykernel the asyncio eventloop is always running::
272 In a Notebook, with ipykernel the asyncio eventloop is always running::
272
273
273 $ jupyter notebook
274 $ jupyter notebook
274 In [1]: import asyncio
275 In [1]: import asyncio
275 ...: loop1 = asyncio.get_event_loop()
276 ...: loop1 = asyncio.get_event_loop()
276 ...: loop1
277 ...: loop1
277 Out[1]: <_UnixSelectorEventLoop running=True closed=False debug=False>
278 Out[1]: <_UnixSelectorEventLoop running=True closed=False debug=False>
278
279
279 In [2]: loop2 = asyncio.get_event_loop()
280 In [2]: loop2 = asyncio.get_event_loop()
280 ...: loop2
281 ...: loop2
281 Out[2]: <_UnixSelectorEventLoop running=True closed=False debug=False>
282 Out[2]: <_UnixSelectorEventLoop running=True closed=False debug=False>
282
283
283 In [3]: loop1 is loop2
284 In [3]: loop1 is loop2
284 Out[3]: True
285 Out[3]: True
285
286
286 In Terminal IPython background tasks are only processed while the foreground
287 In Terminal IPython background tasks are only processed while the foreground
287 task is running, if and only if the foreground task is async::
288 task is running, if and only if the foreground task is async::
288
289
289 $ ipython
290 $ ipython
290 In [1]: import asyncio
291 In [1]: import asyncio
291 ...:
292 ...:
292 ...: async def repeat(msg, n):
293 ...: async def repeat(msg, n):
293 ...: for i in range(n):
294 ...: for i in range(n):
294 ...: print(f"{msg} {i}")
295 ...: print(f"{msg} {i}")
295 ...: await asyncio.sleep(1)
296 ...: await asyncio.sleep(1)
296 ...: return f"{msg} done"
297 ...: return f"{msg} done"
297 ...:
298 ...:
298 ...: asyncio.ensure_future(repeat("background", 10))
299 ...: asyncio.ensure_future(repeat("background", 10))
299 Out[1]: <Task pending coro=<repeat() running at <ipython-input-1-02d0ef250fe7>:3>>
300 Out[1]: <Task pending coro=<repeat() running at <ipython-input-1-02d0ef250fe7>:3>>
300
301
301 In [2]: await asyncio.sleep(3)
302 In [2]: await asyncio.sleep(3)
302 background 0
303 background 0
303 background 1
304 background 1
304 background 2
305 background 2
305 background 3
306 background 3
306
307
307 In [3]: import time
308 In [3]: import time
308 ...: time.sleep(5)
309 ...: time.sleep(5)
309
310
310 In [4]: await asyncio.sleep(3)
311 In [4]: await asyncio.sleep(3)
311 background 4
312 background 4
312 background 5
313 background 5
313 background 6g
314 background 6g
314
315
315 In a Notebook, QtConsole, or any other frontend using IPykernel, background
316 In a Notebook, QtConsole, or any other frontend using IPykernel, background
316 tasks should behave as expected.
317 tasks should behave as expected.
@@ -1,218 +1,225 b''
1 ============
1 ============
2 7.x Series
2 7.x Series
3 ============
3 ============
4
4
5 .. _whatsnew700:
5 .. _whatsnew700:
6
6
7 IPython 7.0.0
7 IPython 7.0.0
8 =============
8 =============
9
9
10 .. warning::
10 .. warning::
11
11
12 IPython 7.0 is currently in Beta. We welcome feedback on API/changes and
12 IPython 7.0 is currently in Beta. We welcome feedback on API/changes and
13 addition/updates to this changelog.
13 addition/updates to this changelog.
14
14
15 Released .... ...., 2018
15 Released .... ...., 2018
16
16
17 IPython 7 include major features improvement as you can read in the following
17 IPython 7 include major features improvement as you can read in the following
18 changelog. This is also the second major version of IPython to support only
18 changelog. This is also the second major version of IPython to support only
19 Python 3 – starting at Python 3.4. Python 2 is still community supported
19 Python 3 – starting at Python 3.4. Python 2 is still community supported
20 on the bugfix only 5.x branch, but we remind you that Python 2 "end of life"
20 on the bugfix only 5.x branch, but we remind you that Python 2 "end of life"
21 is on Jan 1st 2020.
21 is on Jan 1st 2020.
22
22
23 We were able to backport bug fixes to the 5.x branch thanks to our backport bot which
23 We were able to backport bug fixes to the 5.x branch thanks to our backport bot which
24 backported more than `70 Pull-Requests
24 backported more than `70 Pull-Requests
25 <https://github.com/ipython/ipython/pulls?page=3&q=is%3Apr+sort%3Aupdated-desc+author%3Aapp%2Fmeeseeksdev++5.x&utf8=%E2%9C%93>`_, but there are still many PRs that required manually work, and this is an area of the project were you can easily contribute by looking for `PRs still needed backport <https://github.com/ipython/ipython/issues?q=label%3A%22Still+Needs+Manual+Backport%22+is%3Aclosed+sort%3Aupdated-desc>`_
25 <https://github.com/ipython/ipython/pulls?page=3&q=is%3Apr+sort%3Aupdated-desc+author%3Aapp%2Fmeeseeksdev++5.x&utf8=%E2%9C%93>`_, but there are still many PRs that required manually work, and this is an area of the project were you can easily contribute by looking for `PRs still needed backport <https://github.com/ipython/ipython/issues?q=label%3A%22Still+Needs+Manual+Backport%22+is%3Aclosed+sort%3Aupdated-desc>`_
26
26
27 IPython 6.x branch will likely not see any further release unless critical
27 IPython 6.x branch will likely not see any further release unless critical
28 bugs are found.
28 bugs are found.
29
29
30 Make sure you have pip > 9.0 before upgrading. You should be able to update by simply running
30 Make sure you have pip > 9.0 before upgrading. You should be able to update by simply running
31
31
32 .. code::
32 .. code::
33
33
34 pip install ipython --upgrade
34 pip install ipython --upgrade
35
35
36 Or if you have conda installed:
36 Or if you have conda installed:
37
37
38 .. code::
38 .. code::
39
39
40 conda install ipython
40 conda install ipython
41
41
42
42
43
43
44 Prompt Toolkit 2.0
44 Prompt Toolkit 2.0
45 ------------------
45 ------------------
46
46
47 IPython 7.0+ now uses ``prompt_toolkit 2.0``, if you still need to use earlier
47 IPython 7.0+ now uses ``prompt_toolkit 2.0``, if you still need to use earlier
48 ``prompt_toolkit`` version you may need to pin IPython to ``<7.0``.
48 ``prompt_toolkit`` version you may need to pin IPython to ``<7.0``.
49
49
50 Autowait: Asynchronous REPL
50 Autowait: Asynchronous REPL
51 ---------------------------
51 ---------------------------
52
52
53 Staring with IPython 7.0 and on Python 3.6+, IPython can automatically await
53 Staring with IPython 7.0 and on Python 3.6+, IPython can automatically await
54 code at top level, you should not need to access an event loop or runner
54 code at top level, you should not need to access an event loop or runner
55 yourself. To know more read the :ref:`autoawait` section of our docs, see
55 yourself. To know more read the :ref:`autoawait` section of our docs, see
56 :ghpull:`11265` or try the following code::
56 :ghpull:`11265` or try the following code::
57
57
58 Python 3.6.0
58 Python 3.6.0
59 Type 'copyright', 'credits' or 'license' for more information
59 Type 'copyright', 'credits' or 'license' for more information
60 IPython 7.0.0 -- An enhanced Interactive Python. Type '?' for help.
60 IPython 7.0.0 -- An enhanced Interactive Python. Type '?' for help.
61
61
62 In [1]: import aiohttp
62 In [1]: import aiohttp
63 ...: result = aiohttp.get('https://api.github.com')
63 ...: result = aiohttp.get('https://api.github.com')
64
64
65 In [2]: response = await result
65 In [2]: response = await result
66 <pause for a few 100s ms>
66 <pause for a few 100s ms>
67
67
68 In [3]: await response.json()
68 In [3]: await response.json()
69 Out[3]:
69 Out[3]:
70 {'authorizations_url': 'https://api.github.com/authorizations',
70 {'authorizations_url': 'https://api.github.com/authorizations',
71 'code_search_url': 'https://api.github.com/search/code?q={query}{&page,per_page,sort,order}',
71 'code_search_url': 'https://api.github.com/search/code?q={query}{&page,per_page,sort,order}',
72 ...
72 ...
73 }
73 }
74
74
75 .. note::
75 .. note::
76
76
77 Async integration is experimental code, behavior may change or be removed
77 Async integration is experimental code, behavior may change or be removed
78 between Python and IPython versions without warnings.
78 between Python and IPython versions without warnings.
79
79
80 Integration is by default with `asyncio`, but other libraries can be configured,
80 Integration is by default with `asyncio`, but other libraries can be configured,
81 like ``curio`` or ``trio``, to improve concurrency in the REPL::
81 like ``curio`` or ``trio``, to improve concurrency in the REPL::
82
82
83 In [1]: %autoawait trio
83 In [1]: %autoawait trio
84
84
85 In [2]: import trio
85 In [2]: import trio
86
86
87 In [3]: async def child(i):
87 In [3]: async def child(i):
88 ...: print(" child %s goes to sleep"%i)
88 ...: print(" child %s goes to sleep"%i)
89 ...: await trio.sleep(2)
89 ...: await trio.sleep(2)
90 ...: print(" child %s wakes up"%i)
90 ...: print(" child %s wakes up"%i)
91
91
92 In [4]: print('parent start')
92 In [4]: print('parent start')
93 ...: async with trio.open_nursery() as n:
93 ...: async with trio.open_nursery() as n:
94 ...: for i in range(3):
94 ...: for i in range(3):
95 ...: n.spawn(child, i)
95 ...: n.spawn(child, i)
96 ...: print('parent end')
96 ...: print('parent end')
97 parent start
97 parent start
98 child 2 goes to sleep
98 child 2 goes to sleep
99 child 0 goes to sleep
99 child 0 goes to sleep
100 child 1 goes to sleep
100 child 1 goes to sleep
101 <about 2 seconds pause>
101 <about 2 seconds pause>
102 child 2 wakes up
102 child 2 wakes up
103 child 1 wakes up
103 child 1 wakes up
104 child 0 wakes up
104 child 0 wakes up
105 parent end
105 parent end
106
106
107 See :ref:`autoawait` for more information.
107 See :ref:`autoawait` for more information.
108
108
109
109
110 Asynchronous code in a Notebook interface or any other frontend using the
110 Asynchronous code in a Notebook interface or any other frontend using the
111 Jupyter Protocol will need further updates of the IPykernel package.
111 Jupyter Protocol will need further updates of the IPykernel package.
112
112
113 Non-Asynchronous code
113 Non-Asynchronous code
114 ~~~~~~~~~~~~~~~~~~~~~
114 ~~~~~~~~~~~~~~~~~~~~~
115
115
116 As the internal API of IPython are now asynchronous, IPython need to run under
116 As the internal API of IPython are now asynchronous, IPython need to run under
117 an even loop. In order to allow many workflow, (like using the ``%run`` magic,
117 an even loop. In order to allow many workflow, (like using the :magic:`%run`
118 or copy_pasting code that explicitly starts/stop event loop), when top-level code
118 magic, or copy_pasting code that explicitly starts/stop event loop), when
119 is detected as not being asynchronous, IPython code is advanced via a
119 top-level code is detected as not being asynchronous, IPython code is advanced
120 pseudo-synchronous runner, and will not may not advance pending tasks.
120 via a pseudo-synchronous runner, and will not may not advance pending tasks.
121
121
122 Change to Nested Embed
122 Change to Nested Embed
123 ~~~~~~~~~~~~~~~~~~~~~~
123 ~~~~~~~~~~~~~~~~~~~~~~
124
124
125 The introduction of the ability to run async code had some effect on the
125 The introduction of the ability to run async code had some effect on the
126 ``IPython.embed()`` API. By default embed will not allow you to run asynchronous
126 ``IPython.embed()`` API. By default embed will not allow you to run asynchronous
127 code unless a event loop is specified.
127 code unless a event loop is specified.
128
128
129 Effects on Magics
129 Effects on Magics
130 ~~~~~~~~~~~~~~~~~
130 ~~~~~~~~~~~~~~~~~
131
131
132 Some magics will not work with Async, and will need updates. Contribution
132 Some magics will not work with Async, and will need updates. Contribution
133 welcome.
133 welcome.
134
134
135 Expected Future changes
135 Expected Future changes
136 ~~~~~~~~~~~~~~~~~~~~~~~
136 ~~~~~~~~~~~~~~~~~~~~~~~
137
137
138 We expect more internal but public IPython function to become ``async``, and
138 We expect more internal but public IPython function to become ``async``, and
139 will likely end up having a persisting event loop while IPython is running.
139 will likely end up having a persisting event loop while IPython is running.
140
140
141 Thanks
141 Thanks
142 ~~~~~~
142 ~~~~~~
143
143
144 This took more than a year in the making, and the code was rebased a number of
144 This took more than a year in the making, and the code was rebased a number of
145 time leading to commit authorship that may have been lost in the final
145 time leading to commit authorship that may have been lost in the final
146 Pull-Request. Huge thanks to many people for contribution, discussion, code,
146 Pull-Request. Huge thanks to many people for contribution, discussion, code,
147 documentation, use-case: dalejung, danielballan, ellisonbg, fperez, gnestor,
147 documentation, use-case: dalejung, danielballan, ellisonbg, fperez, gnestor,
148 minrk, njsmith, pganssle, tacaswell, takluyver , vidartf ... And many others.
148 minrk, njsmith, pganssle, tacaswell, takluyver , vidartf ... And many others.
149
149
150
150
151 Autoreload Improvement
151 Autoreload Improvement
152 ----------------------
152 ----------------------
153
153
154 The magic ``%autoreload 2`` now captures new methods added to classes. Earlier, only methods existing as of the initial import were being tracked and updated.
154 The magic :magic:`%autoreload 2 <autoreload>` now captures new methods added to
155 classes. Earlier, only methods existing as of the initial import were being
156 tracked and updated.
155
157
156 This new feature helps dual environment development - Jupyter+IDE - where the code gradually moves from notebook cells to package files, as it gets structured.
158 This new feature helps dual environment development - Jupyter+IDE - where the
159 code gradually moves from notebook cells to package files, as it gets
160 structured.
157
161
158 **Example**: An instance of the class `MyClass` will be able to access the method `cube()` after it is uncommented and the file `file1.py` saved on disk.
162 **Example**: An instance of the class ``MyClass`` will be able to access the
163 method ``cube()`` after it is uncommented and the file ``file1.py`` saved on
164 disk.
159
165
160
166
161 ..code::
167 ..code::
162
168
163 # notebook
169 # notebook
164
170
165 from mymodule import MyClass
171 from mymodule import MyClass
166 first = MyClass(5)
172 first = MyClass(5)
167
173
168 .. code::
174 .. code::
169
175
170 # mymodule/file1.py
176 # mymodule/file1.py
171
177
172 class MyClass:
178 class MyClass:
173
179
174 def __init__(self, a=10):
180 def __init__(self, a=10):
175 self.a = a
181 self.a = a
176
182
177 def square(self):
183 def square(self):
178 print('compute square')
184 print('compute square')
179 return self.a*self.a
185 return self.a*self.a
180
186
181 # def cube(self):
187 # def cube(self):
182 # print('compute cube')
188 # print('compute cube')
183 # return self.a*self.a*self.a
189 # return self.a*self.a*self.a
184
190
185
191
186
192
187
193
188 Misc
194 Misc
189 ----
195 ----
190
196
191 The autoindent feature that was deprecated in 5.x was re-enabled and
197 The autoindent feature that was deprecated in 5.x was re-enabled and
192 un-deprecated in :ghpull:`11257`
198 un-deprecated in :ghpull:`11257`
193
199
194 Make ``%run -n -i ...`` work correctly. Earlier, if ``%run`` was passed both arguments, ``-n`` would be silently ignored. See :ghpull:`10308`
200 Make :magic:`%run -n -i ... <run>` work correctly. Earlier, if :magic:`%run` was
201 passed both arguments, ``-n`` would be silently ignored. See :ghpull:`10308`
195
202
196
203
197 The ``%%script`` (as well as ``%%bash``, ``ruby``... ) cell magics now raise
204 The :cellmagic:`%%script`` (as well as :cellmagic:`%%bash``,
198 by default if the return code of the given code is non-zero (thus halting
205 :cellmagic:`%%ruby``... ) cell magics now raise by default if the return code of
199 execution of further cells in a notebook). The behavior can be disable by
206 the given code is non-zero (thus halting execution of further cells in a
200 passing the ``--no-raise-error`` flag.
207 notebook). The behavior can be disable by passing the ``--no-raise-error`` flag.
201
208
202
209
203 Deprecations
210 Deprecations
204 ------------
211 ------------
205
212
206 A couple of unused function and methods have been deprecated and will be removed
213 A couple of unused function and methods have been deprecated and will be removed
207 in future versions:
214 in future versions:
208
215
209 - ``IPython.utils.io.raw_print_err``
216 - ``IPython.utils.io.raw_print_err``
210 - ``IPython.utils.io.raw_print``
217 - ``IPython.utils.io.raw_print``
211
218
212
219
213 Backwards incompatible changes
220 Backwards incompatible changes
214 ------------------------------
221 ------------------------------
215
222
216 * The API for transforming input before it is parsed as Python code has been
223 * The API for transforming input before it is parsed as Python code has been
217 completely redesigned, and any custom input transformations will need to be
224 completely redesigned, and any custom input transformations will need to be
218 rewritten. See :doc:`/config/inputtransforms` for details of the new API.
225 rewritten. See :doc:`/config/inputtransforms` for details of the new API.
@@ -1,157 +1,160 b''
1 """Define text roles for GitHub
1 """Define text roles for GitHub
2
2
3 * ghissue - Issue
3 * ghissue - Issue
4 * ghpull - Pull Request
4 * ghpull - Pull Request
5 * ghuser - User
5 * ghuser - User
6
6
7 Adapted from bitbucket example here:
7 Adapted from bitbucket example here:
8 https://bitbucket.org/birkenfeld/sphinx-contrib/src/tip/bitbucket/sphinxcontrib/bitbucket.py
8 https://bitbucket.org/birkenfeld/sphinx-contrib/src/tip/bitbucket/sphinxcontrib/bitbucket.py
9
9
10 Authors
10 Authors
11 -------
11 -------
12
12
13 * Doug Hellmann
13 * Doug Hellmann
14 * Min RK
14 * Min RK
15 """
15 """
16 #
16 #
17 # Original Copyright (c) 2010 Doug Hellmann. All rights reserved.
17 # Original Copyright (c) 2010 Doug Hellmann. All rights reserved.
18 #
18 #
19
19
20 from docutils import nodes, utils
20 from docutils import nodes, utils
21 from docutils.parsers.rst.roles import set_classes
21 from docutils.parsers.rst.roles import set_classes
22 from sphinx.util.logging import getLogger
23
24 info = getLogger(__name__).info
22
25
23 def make_link_node(rawtext, app, type, slug, options):
26 def make_link_node(rawtext, app, type, slug, options):
24 """Create a link to a github resource.
27 """Create a link to a github resource.
25
28
26 :param rawtext: Text being replaced with link node.
29 :param rawtext: Text being replaced with link node.
27 :param app: Sphinx application context
30 :param app: Sphinx application context
28 :param type: Link type (issues, changeset, etc.)
31 :param type: Link type (issues, changeset, etc.)
29 :param slug: ID of the thing to link to
32 :param slug: ID of the thing to link to
30 :param options: Options dictionary passed to role func.
33 :param options: Options dictionary passed to role func.
31 """
34 """
32
35
33 try:
36 try:
34 base = app.config.github_project_url
37 base = app.config.github_project_url
35 if not base:
38 if not base:
36 raise AttributeError
39 raise AttributeError
37 if not base.endswith('/'):
40 if not base.endswith('/'):
38 base += '/'
41 base += '/'
39 except AttributeError as err:
42 except AttributeError as err:
40 raise ValueError('github_project_url configuration value is not set (%s)' % str(err))
43 raise ValueError('github_project_url configuration value is not set (%s)' % str(err))
41
44
42 ref = base + type + '/' + slug + '/'
45 ref = base + type + '/' + slug + '/'
43 set_classes(options)
46 set_classes(options)
44 prefix = "#"
47 prefix = "#"
45 if type == 'pull':
48 if type == 'pull':
46 prefix = "PR " + prefix
49 prefix = "PR " + prefix
47 node = nodes.reference(rawtext, prefix + utils.unescape(slug), refuri=ref,
50 node = nodes.reference(rawtext, prefix + utils.unescape(slug), refuri=ref,
48 **options)
51 **options)
49 return node
52 return node
50
53
51 def ghissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
54 def ghissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
52 """Link to a GitHub issue.
55 """Link to a GitHub issue.
53
56
54 Returns 2 part tuple containing list of nodes to insert into the
57 Returns 2 part tuple containing list of nodes to insert into the
55 document and a list of system messages. Both are allowed to be
58 document and a list of system messages. Both are allowed to be
56 empty.
59 empty.
57
60
58 :param name: The role name used in the document.
61 :param name: The role name used in the document.
59 :param rawtext: The entire markup snippet, with role.
62 :param rawtext: The entire markup snippet, with role.
60 :param text: The text marked with the role.
63 :param text: The text marked with the role.
61 :param lineno: The line number where rawtext appears in the input.
64 :param lineno: The line number where rawtext appears in the input.
62 :param inliner: The inliner instance that called us.
65 :param inliner: The inliner instance that called us.
63 :param options: Directive options for customization.
66 :param options: Directive options for customization.
64 :param content: The directive content for customization.
67 :param content: The directive content for customization.
65 """
68 """
66
69
67 try:
70 try:
68 issue_num = int(text)
71 issue_num = int(text)
69 if issue_num <= 0:
72 if issue_num <= 0:
70 raise ValueError
73 raise ValueError
71 except ValueError:
74 except ValueError:
72 msg = inliner.reporter.error(
75 msg = inliner.reporter.error(
73 'GitHub issue number must be a number greater than or equal to 1; '
76 'GitHub issue number must be a number greater than or equal to 1; '
74 '"%s" is invalid.' % text, line=lineno)
77 '"%s" is invalid.' % text, line=lineno)
75 prb = inliner.problematic(rawtext, rawtext, msg)
78 prb = inliner.problematic(rawtext, rawtext, msg)
76 return [prb], [msg]
79 return [prb], [msg]
77 app = inliner.document.settings.env.app
80 app = inliner.document.settings.env.app
78 #app.info('issue %r' % text)
81 #info('issue %r' % text)
79 if 'pull' in name.lower():
82 if 'pull' in name.lower():
80 category = 'pull'
83 category = 'pull'
81 elif 'issue' in name.lower():
84 elif 'issue' in name.lower():
82 category = 'issues'
85 category = 'issues'
83 else:
86 else:
84 msg = inliner.reporter.error(
87 msg = inliner.reporter.error(
85 'GitHub roles include "ghpull" and "ghissue", '
88 'GitHub roles include "ghpull" and "ghissue", '
86 '"%s" is invalid.' % name, line=lineno)
89 '"%s" is invalid.' % name, line=lineno)
87 prb = inliner.problematic(rawtext, rawtext, msg)
90 prb = inliner.problematic(rawtext, rawtext, msg)
88 return [prb], [msg]
91 return [prb], [msg]
89 node = make_link_node(rawtext, app, category, str(issue_num), options)
92 node = make_link_node(rawtext, app, category, str(issue_num), options)
90 return [node], []
93 return [node], []
91
94
92 def ghuser_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
95 def ghuser_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
93 """Link to a GitHub user.
96 """Link to a GitHub user.
94
97
95 Returns 2 part tuple containing list of nodes to insert into the
98 Returns 2 part tuple containing list of nodes to insert into the
96 document and a list of system messages. Both are allowed to be
99 document and a list of system messages. Both are allowed to be
97 empty.
100 empty.
98
101
99 :param name: The role name used in the document.
102 :param name: The role name used in the document.
100 :param rawtext: The entire markup snippet, with role.
103 :param rawtext: The entire markup snippet, with role.
101 :param text: The text marked with the role.
104 :param text: The text marked with the role.
102 :param lineno: The line number where rawtext appears in the input.
105 :param lineno: The line number where rawtext appears in the input.
103 :param inliner: The inliner instance that called us.
106 :param inliner: The inliner instance that called us.
104 :param options: Directive options for customization.
107 :param options: Directive options for customization.
105 :param content: The directive content for customization.
108 :param content: The directive content for customization.
106 """
109 """
107 app = inliner.document.settings.env.app
110 app = inliner.document.settings.env.app
108 #app.info('user link %r' % text)
111 #info('user link %r' % text)
109 ref = 'https://www.github.com/' + text
112 ref = 'https://www.github.com/' + text
110 node = nodes.reference(rawtext, text, refuri=ref, **options)
113 node = nodes.reference(rawtext, text, refuri=ref, **options)
111 return [node], []
114 return [node], []
112
115
113 def ghcommit_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
116 def ghcommit_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
114 """Link to a GitHub commit.
117 """Link to a GitHub commit.
115
118
116 Returns 2 part tuple containing list of nodes to insert into the
119 Returns 2 part tuple containing list of nodes to insert into the
117 document and a list of system messages. Both are allowed to be
120 document and a list of system messages. Both are allowed to be
118 empty.
121 empty.
119
122
120 :param name: The role name used in the document.
123 :param name: The role name used in the document.
121 :param rawtext: The entire markup snippet, with role.
124 :param rawtext: The entire markup snippet, with role.
122 :param text: The text marked with the role.
125 :param text: The text marked with the role.
123 :param lineno: The line number where rawtext appears in the input.
126 :param lineno: The line number where rawtext appears in the input.
124 :param inliner: The inliner instance that called us.
127 :param inliner: The inliner instance that called us.
125 :param options: Directive options for customization.
128 :param options: Directive options for customization.
126 :param content: The directive content for customization.
129 :param content: The directive content for customization.
127 """
130 """
128 app = inliner.document.settings.env.app
131 app = inliner.document.settings.env.app
129 #app.info('user link %r' % text)
132 #info('user link %r' % text)
130 try:
133 try:
131 base = app.config.github_project_url
134 base = app.config.github_project_url
132 if not base:
135 if not base:
133 raise AttributeError
136 raise AttributeError
134 if not base.endswith('/'):
137 if not base.endswith('/'):
135 base += '/'
138 base += '/'
136 except AttributeError as err:
139 except AttributeError as err:
137 raise ValueError('github_project_url configuration value is not set (%s)' % str(err))
140 raise ValueError('github_project_url configuration value is not set (%s)' % str(err))
138
141
139 ref = base + text
142 ref = base + text
140 node = nodes.reference(rawtext, text[:6], refuri=ref, **options)
143 node = nodes.reference(rawtext, text[:6], refuri=ref, **options)
141 return [node], []
144 return [node], []
142
145
143
146
144 def setup(app):
147 def setup(app):
145 """Install the plugin.
148 """Install the plugin.
146
149
147 :param app: Sphinx application context.
150 :param app: Sphinx application context.
148 """
151 """
149 app.info('Initializing GitHub plugin')
152 info('Initializing GitHub plugin')
150 app.add_role('ghissue', ghissue_role)
153 app.add_role('ghissue', ghissue_role)
151 app.add_role('ghpull', ghissue_role)
154 app.add_role('ghpull', ghissue_role)
152 app.add_role('ghuser', ghuser_role)
155 app.add_role('ghuser', ghuser_role)
153 app.add_role('ghcommit', ghcommit_role)
156 app.add_role('ghcommit', ghcommit_role)
154 app.add_config_value('github_project_url', None, 'env')
157 app.add_config_value('github_project_url', None, 'env')
155
158
156 metadata = {'parallel_read_safe': True, 'parallel_write_safe': True}
159 metadata = {'parallel_read_safe': True, 'parallel_write_safe': True}
157 return metadata
160 return metadata
@@ -1,45 +1,46 b''
1 import re
1 import re
2 from sphinx import addnodes
2 from sphinx import addnodes
3 from sphinx.domains.std import StandardDomain
3 from sphinx.domains.std import StandardDomain
4 from sphinx.roles import XRefRole
4 from sphinx.roles import XRefRole
5
5
6 name_re = re.compile(r"[\w_]+")
6 name_re = re.compile(r"[\w_]+")
7
7
8 def parse_magic(env, sig, signode):
8 def parse_magic(env, sig, signode):
9 m = name_re.match(sig)
9 m = name_re.match(sig)
10 if not m:
10 if not m:
11 raise Exception("Invalid magic command: %s" % sig)
11 raise Exception("Invalid magic command: %s" % sig)
12 name = "%" + sig
12 name = "%" + sig
13 signode += addnodes.desc_name(name, name)
13 signode += addnodes.desc_name(name, name)
14 return m.group(0)
14 return m.group(0)
15
15
16 class LineMagicRole(XRefRole):
16 class LineMagicRole(XRefRole):
17 """Cross reference role displayed with a % prefix"""
17 """Cross reference role displayed with a % prefix"""
18 prefix = "%"
18 prefix = "%"
19
19
20 def process_link(self, env, refnode, has_explicit_title, title, target):
20 def process_link(self, env, refnode, has_explicit_title, title, target):
21 if not has_explicit_title:
21 if not has_explicit_title:
22 title = self.prefix + title.lstrip("%")
22 title = self.prefix + title.lstrip("%")
23 target = target.lstrip("%")
23 target = target.lstrip("%")
24 return title, target
24 return title, target
25
25
26 def parse_cell_magic(env, sig, signode):
26 def parse_cell_magic(env, sig, signode):
27 m = name_re.match(sig)
27 m = name_re.match(sig)
28 if not m:
28 if not m:
29 raise ValueError("Invalid cell magic: %s" % sig)
29 raise ValueError("Invalid cell magic: %s" % sig)
30 name = "%%" + sig
30 name = "%%" + sig
31 signode += addnodes.desc_name(name, name)
31 signode += addnodes.desc_name(name, name)
32 return m.group(0)
32 return m.group(0)
33
33
34 class CellMagicRole(LineMagicRole):
34 class CellMagicRole(LineMagicRole):
35 """Cross reference role displayed with a %% prefix"""
35 """Cross reference role displayed with a %% prefix"""
36 prefix = "%%"
36 prefix = "%%"
37
37
38 def setup(app):
38 def setup(app):
39 app.add_object_type('magic', 'magic', 'pair: %s; magic command', parse_magic)
39 app.add_object_type('magic', 'magic', 'pair: %s; magic command', parse_magic)
40 StandardDomain.roles['magic'] = LineMagicRole()
40 app.add_role_to_domain('std', 'magic', LineMagicRole(), override=True)
41
41 app.add_object_type('cellmagic', 'cellmagic', 'pair: %s; cell magic', parse_cell_magic)
42 app.add_object_type('cellmagic', 'cellmagic', 'pair: %s; cell magic', parse_cell_magic)
42 StandardDomain.roles['cellmagic'] = CellMagicRole()
43 app.add_role_to_domain('std', 'cellmagic', CellMagicRole(), override=True)
43
44
44 metadata = {'parallel_read_safe': True, 'parallel_write_safe': True}
45 metadata = {'parallel_read_safe': True, 'parallel_write_safe': True}
45 return metadata
46 return metadata
General Comments 0
You need to be logged in to leave comments. Login now