Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- temp_venv/lib/python3.13/site-packages/IPython/core/__pycache__/alias.cpython-313.pyc +0 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/__pycache__/crashhandler.cpython-313.pyc +0 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/__pycache__/display.cpython-313.pyc +0 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/__pycache__/display_trap.cpython-313.pyc +0 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/__pycache__/extensions.cpython-313.pyc +0 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/__pycache__/getipython.cpython-313.pyc +0 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/__pycache__/oinspect.cpython-313.pyc +0 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/__pycache__/payload.cpython-313.pyc +0 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/__pycache__/ultratb.cpython-313.pyc +0 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/magics/__init__.py +42 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/magics/ast_mod.py +330 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/magics/auto.py +144 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/magics/basic.py +674 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/magics/code.py +757 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/magics/config.py +140 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/magics/display.py +93 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/magics/execution.py +1677 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/magics/extension.py +63 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/magics/history.py +338 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/magics/logging.py +195 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/magics/namespace.py +711 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/magics/osm.py +855 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/magics/packaging.py +181 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/magics/pylab.py +173 -0
- temp_venv/lib/python3.13/site-packages/IPython/core/magics/script.py +393 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/__pycache__/__init__.cpython-313.pyc +0 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/__pycache__/debugger.cpython-313.pyc +0 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/__pycache__/embed.cpython-313.pyc +0 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/__pycache__/interactiveshell.cpython-313.pyc +0 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/__pycache__/ipapp.cpython-313.pyc +0 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/__pycache__/magics.cpython-313.pyc +0 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/__pycache__/prompts.cpython-313.pyc +0 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/__pycache__/ptutils.cpython-313.pyc +0 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/__init__.py +139 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/__pycache__/__init__.cpython-313.pyc +0 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/asyncio.py +40 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/glut.py +140 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/gtk.py +60 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/gtk3.py +13 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/gtk4.py +27 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/osx.py +147 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/pyglet.py +67 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/qt.py +90 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/tk.py +93 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/wx.py +219 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/shortcuts/__init__.py +636 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/shortcuts/__pycache__/__init__.cpython-313.pyc +0 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/shortcuts/__pycache__/auto_match.cpython-313.pyc +0 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/shortcuts/__pycache__/auto_suggest.cpython-313.pyc +0 -0
- temp_venv/lib/python3.13/site-packages/IPython/terminal/shortcuts/__pycache__/filters.cpython-313.pyc +0 -0
temp_venv/lib/python3.13/site-packages/IPython/core/__pycache__/alias.cpython-313.pyc
ADDED
Binary file (10.8 kB). View file
|
|
temp_venv/lib/python3.13/site-packages/IPython/core/__pycache__/crashhandler.cpython-313.pyc
ADDED
Binary file (9.11 kB). View file
|
|
temp_venv/lib/python3.13/site-packages/IPython/core/__pycache__/display.cpython-313.pyc
ADDED
Binary file (47.8 kB). View file
|
|
temp_venv/lib/python3.13/site-packages/IPython/core/__pycache__/display_trap.cpython-313.pyc
ADDED
Binary file (2.61 kB). View file
|
|
temp_venv/lib/python3.13/site-packages/IPython/core/__pycache__/extensions.cpython-313.pyc
ADDED
Binary file (6.29 kB). View file
|
|
temp_venv/lib/python3.13/site-packages/IPython/core/__pycache__/getipython.cpython-313.pyc
ADDED
Binary file (704 Bytes). View file
|
|
temp_venv/lib/python3.13/site-packages/IPython/core/__pycache__/oinspect.cpython-313.pyc
ADDED
Binary file (42 kB). View file
|
|
temp_venv/lib/python3.13/site-packages/IPython/core/__pycache__/payload.cpython-313.pyc
ADDED
Binary file (1.91 kB). View file
|
|
temp_venv/lib/python3.13/site-packages/IPython/core/__pycache__/ultratb.cpython-313.pyc
ADDED
Binary file (47.2 kB). View file
|
|
temp_venv/lib/python3.13/site-packages/IPython/core/magics/__init__.py
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Implementation of all the magic functions built into IPython.
|
2 |
+
"""
|
3 |
+
#-----------------------------------------------------------------------------
|
4 |
+
# Copyright (c) 2012 The IPython Development Team.
|
5 |
+
#
|
6 |
+
# Distributed under the terms of the Modified BSD License.
|
7 |
+
#
|
8 |
+
# The full license is in the file COPYING.txt, distributed with this software.
|
9 |
+
#-----------------------------------------------------------------------------
|
10 |
+
|
11 |
+
#-----------------------------------------------------------------------------
|
12 |
+
# Imports
|
13 |
+
#-----------------------------------------------------------------------------
|
14 |
+
|
15 |
+
from ..magic import Magics, magics_class
|
16 |
+
from .auto import AutoMagics
|
17 |
+
from .basic import BasicMagics, AsyncMagics
|
18 |
+
from .code import CodeMagics, MacroToEdit
|
19 |
+
from .config import ConfigMagics
|
20 |
+
from .display import DisplayMagics
|
21 |
+
from .execution import ExecutionMagics
|
22 |
+
from .extension import ExtensionMagics
|
23 |
+
from .history import HistoryMagics
|
24 |
+
from .logging import LoggingMagics
|
25 |
+
from .namespace import NamespaceMagics
|
26 |
+
from .osm import OSMagics
|
27 |
+
from .packaging import PackagingMagics
|
28 |
+
from .pylab import PylabMagics
|
29 |
+
from .script import ScriptMagics
|
30 |
+
|
31 |
+
#-----------------------------------------------------------------------------
|
32 |
+
# Magic implementation classes
|
33 |
+
#-----------------------------------------------------------------------------
|
34 |
+
|
35 |
+
@magics_class
|
36 |
+
class UserMagics(Magics):
|
37 |
+
"""Placeholder for user-defined magics to be added at runtime.
|
38 |
+
|
39 |
+
All magics are eventually merged into a single namespace at runtime, but we
|
40 |
+
use this class to isolate the magics defined dynamically by the user into
|
41 |
+
their own class.
|
42 |
+
"""
|
temp_venv/lib/python3.13/site-packages/IPython/core/magics/ast_mod.py
ADDED
@@ -0,0 +1,330 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
This module contains utility function and classes to inject simple ast
|
3 |
+
transformations based on code strings into IPython. While it is already possible
|
4 |
+
with ast-transformers it is not easy to directly manipulate ast.
|
5 |
+
|
6 |
+
|
7 |
+
IPython has pre-code and post-code hooks, but are ran from within the IPython
|
8 |
+
machinery so may be inappropriate, for example for performance measurement.
|
9 |
+
|
10 |
+
This module give you tools to simplify this, and expose 2 classes:
|
11 |
+
|
12 |
+
- `ReplaceCodeTransformer` which is a simple ast transformer based on code
|
13 |
+
template,
|
14 |
+
|
15 |
+
and for advance case:
|
16 |
+
|
17 |
+
- `Mangler` which is a simple ast transformer that mangle names in the ast.
|
18 |
+
|
19 |
+
|
20 |
+
Example, let's try to make a simple version of the ``timeit`` magic, that run a
|
21 |
+
code snippet 10 times and print the average time taken.
|
22 |
+
|
23 |
+
Basically we want to run :
|
24 |
+
|
25 |
+
.. code-block:: python
|
26 |
+
|
27 |
+
from time import perf_counter
|
28 |
+
now = perf_counter()
|
29 |
+
for i in range(10):
|
30 |
+
__code__ # our code
|
31 |
+
print(f"Time taken: {(perf_counter() - now)/10}")
|
32 |
+
__ret__ # the result of the last statement
|
33 |
+
|
34 |
+
Where ``__code__`` is the code snippet we want to run, and ``__ret__`` is the
|
35 |
+
result, so that if we for example run `dataframe.head()` IPython still display
|
36 |
+
the head of dataframe instead of nothing.
|
37 |
+
|
38 |
+
Here is a complete example of a file `timit2.py` that define such a magic:
|
39 |
+
|
40 |
+
.. code-block:: python
|
41 |
+
|
42 |
+
from IPython.core.magic import (
|
43 |
+
Magics,
|
44 |
+
magics_class,
|
45 |
+
line_cell_magic,
|
46 |
+
)
|
47 |
+
from IPython.core.magics.ast_mod import ReplaceCodeTransformer
|
48 |
+
from textwrap import dedent
|
49 |
+
import ast
|
50 |
+
|
51 |
+
template = template = dedent('''
|
52 |
+
from time import perf_counter
|
53 |
+
now = perf_counter()
|
54 |
+
for i in range(10):
|
55 |
+
__code__
|
56 |
+
print(f"Time taken: {(perf_counter() - now)/10}")
|
57 |
+
__ret__
|
58 |
+
'''
|
59 |
+
)
|
60 |
+
|
61 |
+
|
62 |
+
@magics_class
|
63 |
+
class AstM(Magics):
|
64 |
+
@line_cell_magic
|
65 |
+
def t2(self, line, cell):
|
66 |
+
transformer = ReplaceCodeTransformer.from_string(template)
|
67 |
+
transformer.debug = True
|
68 |
+
transformer.mangler.debug = True
|
69 |
+
new_code = transformer.visit(ast.parse(cell))
|
70 |
+
return exec(compile(new_code, "<ast>", "exec"))
|
71 |
+
|
72 |
+
|
73 |
+
def load_ipython_extension(ip):
|
74 |
+
ip.register_magics(AstM)
|
75 |
+
|
76 |
+
|
77 |
+
|
78 |
+
.. code-block:: python
|
79 |
+
|
80 |
+
In [1]: %load_ext timit2
|
81 |
+
|
82 |
+
In [2]: %%t2
|
83 |
+
...: import time
|
84 |
+
...: time.sleep(0.05)
|
85 |
+
...:
|
86 |
+
...:
|
87 |
+
Time taken: 0.05435649999999441
|
88 |
+
|
89 |
+
|
90 |
+
If you wish to ran all the code enter in IPython in an ast transformer, you can
|
91 |
+
do so as well:
|
92 |
+
|
93 |
+
.. code-block:: python
|
94 |
+
|
95 |
+
In [1]: from IPython.core.magics.ast_mod import ReplaceCodeTransformer
|
96 |
+
...:
|
97 |
+
...: template = '''
|
98 |
+
...: from time import perf_counter
|
99 |
+
...: now = perf_counter()
|
100 |
+
...: __code__
|
101 |
+
...: print(f"Code ran in {perf_counter()-now}")
|
102 |
+
...: __ret__'''
|
103 |
+
...:
|
104 |
+
...: get_ipython().ast_transformers.append(ReplaceCodeTransformer.from_string(template))
|
105 |
+
|
106 |
+
In [2]: 1+1
|
107 |
+
Code ran in 3.40410006174352e-05
|
108 |
+
Out[2]: 2
|
109 |
+
|
110 |
+
|
111 |
+
|
112 |
+
Hygiene and Mangling
|
113 |
+
--------------------
|
114 |
+
|
115 |
+
The ast transformer above is not hygienic, it may not work if the user code use
|
116 |
+
the same variable names as the ones used in the template. For example.
|
117 |
+
|
118 |
+
To help with this by default the `ReplaceCodeTransformer` will mangle all names
|
119 |
+
staring with 3 underscores. This is a simple heuristic that should work in most
|
120 |
+
case, but can be cumbersome in some case. We provide a `Mangler` class that can
|
121 |
+
be overridden to change the mangling heuristic, or simply use the `mangle_all`
|
122 |
+
utility function. It will _try_ to mangle all names (except `__ret__` and
|
123 |
+
`__code__`), but this include builtins (``print``, ``range``, ``type``) and
|
124 |
+
replace those by invalid identifiers py prepending ``mangle-``:
|
125 |
+
``mangle-print``, ``mangle-range``, ``mangle-type`` etc. This is not a problem
|
126 |
+
as currently Python AST support invalid identifiers, but it may not be the case
|
127 |
+
in the future.
|
128 |
+
|
129 |
+
You can set `ReplaceCodeTransformer.debug=True` and
|
130 |
+
`ReplaceCodeTransformer.mangler.debug=True` to see the code after mangling and
|
131 |
+
transforming:
|
132 |
+
|
133 |
+
.. code-block:: python
|
134 |
+
|
135 |
+
|
136 |
+
In [1]: from IPython.core.magics.ast_mod import ReplaceCodeTransformer, mangle_all
|
137 |
+
...:
|
138 |
+
...: template = '''
|
139 |
+
...: from builtins import type, print
|
140 |
+
...: from time import perf_counter
|
141 |
+
...: now = perf_counter()
|
142 |
+
...: __code__
|
143 |
+
...: print(f"Code ran in {perf_counter()-now}")
|
144 |
+
...: __ret__'''
|
145 |
+
...:
|
146 |
+
...: transformer = ReplaceCodeTransformer.from_string(template, mangling_predicate=mangle_all)
|
147 |
+
|
148 |
+
|
149 |
+
In [2]: transformer.debug = True
|
150 |
+
...: transformer.mangler.debug = True
|
151 |
+
...: get_ipython().ast_transformers.append(transformer)
|
152 |
+
|
153 |
+
In [3]: 1+1
|
154 |
+
Mangling Alias mangle-type
|
155 |
+
Mangling Alias mangle-print
|
156 |
+
Mangling Alias mangle-perf_counter
|
157 |
+
Mangling now
|
158 |
+
Mangling perf_counter
|
159 |
+
Not mangling __code__
|
160 |
+
Mangling print
|
161 |
+
Mangling perf_counter
|
162 |
+
Mangling now
|
163 |
+
Not mangling __ret__
|
164 |
+
---- Transformed code ----
|
165 |
+
from builtins import type as mangle-type, print as mangle-print
|
166 |
+
from time import perf_counter as mangle-perf_counter
|
167 |
+
mangle-now = mangle-perf_counter()
|
168 |
+
ret-tmp = 1 + 1
|
169 |
+
mangle-print(f'Code ran in {mangle-perf_counter() - mangle-now}')
|
170 |
+
ret-tmp
|
171 |
+
---- ---------------- ----
|
172 |
+
Code ran in 0.00013654199938173406
|
173 |
+
Out[3]: 2
|
174 |
+
|
175 |
+
|
176 |
+
"""
|
177 |
+
|
178 |
+
__skip_doctest__ = True
|
179 |
+
|
180 |
+
|
181 |
+
from ast import (
|
182 |
+
NodeTransformer,
|
183 |
+
Store,
|
184 |
+
Load,
|
185 |
+
Name,
|
186 |
+
Expr,
|
187 |
+
Assign,
|
188 |
+
Module,
|
189 |
+
Import,
|
190 |
+
ImportFrom,
|
191 |
+
)
|
192 |
+
import ast
|
193 |
+
import copy
|
194 |
+
|
195 |
+
from typing import Dict, Optional, Union
|
196 |
+
|
197 |
+
|
198 |
+
mangle_all = lambda name: False if name in ("__ret__", "__code__") else True
|
199 |
+
|
200 |
+
|
201 |
+
class Mangler(NodeTransformer):
|
202 |
+
"""
|
203 |
+
Mangle given names in and ast tree to make sure they do not conflict with
|
204 |
+
user code.
|
205 |
+
"""
|
206 |
+
|
207 |
+
enabled: bool = True
|
208 |
+
debug: bool = False
|
209 |
+
|
210 |
+
def log(self, *args, **kwargs):
|
211 |
+
if self.debug:
|
212 |
+
print(*args, **kwargs)
|
213 |
+
|
214 |
+
def __init__(self, predicate=None):
|
215 |
+
if predicate is None:
|
216 |
+
predicate = lambda name: name.startswith("___")
|
217 |
+
self.predicate = predicate
|
218 |
+
|
219 |
+
def visit_Name(self, node):
|
220 |
+
if self.predicate(node.id):
|
221 |
+
self.log("Mangling", node.id)
|
222 |
+
# Once in the ast we do not need
|
223 |
+
# names to be valid identifiers.
|
224 |
+
node.id = "mangle-" + node.id
|
225 |
+
else:
|
226 |
+
self.log("Not mangling", node.id)
|
227 |
+
return node
|
228 |
+
|
229 |
+
def visit_FunctionDef(self, node):
|
230 |
+
if self.predicate(node.name):
|
231 |
+
self.log("Mangling", node.name)
|
232 |
+
node.name = "mangle-" + node.name
|
233 |
+
else:
|
234 |
+
self.log("Not mangling", node.name)
|
235 |
+
|
236 |
+
for arg in node.args.args:
|
237 |
+
if self.predicate(arg.arg):
|
238 |
+
self.log("Mangling function arg", arg.arg)
|
239 |
+
arg.arg = "mangle-" + arg.arg
|
240 |
+
else:
|
241 |
+
self.log("Not mangling function arg", arg.arg)
|
242 |
+
return self.generic_visit(node)
|
243 |
+
|
244 |
+
def visit_ImportFrom(self, node: ImportFrom):
|
245 |
+
return self._visit_Import_and_ImportFrom(node)
|
246 |
+
|
247 |
+
def visit_Import(self, node: Import):
|
248 |
+
return self._visit_Import_and_ImportFrom(node)
|
249 |
+
|
250 |
+
def _visit_Import_and_ImportFrom(self, node: Union[Import, ImportFrom]):
|
251 |
+
for alias in node.names:
|
252 |
+
asname = alias.name if alias.asname is None else alias.asname
|
253 |
+
if self.predicate(asname):
|
254 |
+
new_name: str = "mangle-" + asname
|
255 |
+
self.log("Mangling Alias", new_name)
|
256 |
+
alias.asname = new_name
|
257 |
+
else:
|
258 |
+
self.log("Not mangling Alias", alias.asname)
|
259 |
+
return node
|
260 |
+
|
261 |
+
|
262 |
+
class ReplaceCodeTransformer(NodeTransformer):
|
263 |
+
enabled: bool = True
|
264 |
+
debug: bool = False
|
265 |
+
mangler: Mangler
|
266 |
+
|
267 |
+
def __init__(
|
268 |
+
self, template: Module, mapping: Optional[Dict] = None, mangling_predicate=None
|
269 |
+
):
|
270 |
+
assert isinstance(mapping, (dict, type(None)))
|
271 |
+
assert isinstance(mangling_predicate, (type(None), type(lambda: None)))
|
272 |
+
assert isinstance(template, ast.Module)
|
273 |
+
self.template = template
|
274 |
+
self.mangler = Mangler(predicate=mangling_predicate)
|
275 |
+
if mapping is None:
|
276 |
+
mapping = {}
|
277 |
+
self.mapping = mapping
|
278 |
+
|
279 |
+
@classmethod
|
280 |
+
def from_string(
|
281 |
+
cls, template: str, mapping: Optional[Dict] = None, mangling_predicate=None
|
282 |
+
):
|
283 |
+
return cls(
|
284 |
+
ast.parse(template), mapping=mapping, mangling_predicate=mangling_predicate
|
285 |
+
)
|
286 |
+
|
287 |
+
def visit_Module(self, code):
|
288 |
+
if not self.enabled:
|
289 |
+
return code
|
290 |
+
# if not isinstance(code, ast.Module):
|
291 |
+
# recursively called...
|
292 |
+
# return generic_visit(self, code)
|
293 |
+
last = code.body[-1]
|
294 |
+
if isinstance(last, Expr):
|
295 |
+
code.body.pop()
|
296 |
+
code.body.append(Assign([Name("ret-tmp", ctx=Store())], value=last.value))
|
297 |
+
ast.fix_missing_locations(code)
|
298 |
+
ret = Expr(value=Name("ret-tmp", ctx=Load()))
|
299 |
+
ret = ast.fix_missing_locations(ret)
|
300 |
+
self.mapping["__ret__"] = ret
|
301 |
+
else:
|
302 |
+
self.mapping["__ret__"] = ast.parse("None").body[0]
|
303 |
+
self.mapping["__code__"] = code.body
|
304 |
+
tpl = ast.fix_missing_locations(self.template)
|
305 |
+
|
306 |
+
tx = copy.deepcopy(tpl)
|
307 |
+
tx = self.mangler.visit(tx)
|
308 |
+
node = self.generic_visit(tx)
|
309 |
+
node_2 = ast.fix_missing_locations(node)
|
310 |
+
if self.debug:
|
311 |
+
print("---- Transformed code ----")
|
312 |
+
print(ast.unparse(node_2))
|
313 |
+
print("---- ---------------- ----")
|
314 |
+
return node_2
|
315 |
+
|
316 |
+
# this does not work as the name might be in a list and one might want to extend the list.
|
317 |
+
# def visit_Name(self, name):
|
318 |
+
# if name.id in self.mapping and name.id == "__ret__":
|
319 |
+
# print(name, "in mapping")
|
320 |
+
# if isinstance(name.ctx, ast.Store):
|
321 |
+
# return Name("tmp", ctx=Store())
|
322 |
+
# else:
|
323 |
+
# return copy.deepcopy(self.mapping[name.id])
|
324 |
+
# return name
|
325 |
+
|
326 |
+
def visit_Expr(self, expr):
|
327 |
+
if isinstance(expr.value, Name) and expr.value.id in self.mapping:
|
328 |
+
if self.mapping[expr.value.id] is not None:
|
329 |
+
return copy.deepcopy(self.mapping[expr.value.id])
|
330 |
+
return self.generic_visit(expr)
|
temp_venv/lib/python3.13/site-packages/IPython/core/magics/auto.py
ADDED
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Implementation of magic functions that control various automatic behaviors.
|
2 |
+
"""
|
3 |
+
#-----------------------------------------------------------------------------
|
4 |
+
# Copyright (c) 2012 The IPython Development Team.
|
5 |
+
#
|
6 |
+
# Distributed under the terms of the Modified BSD License.
|
7 |
+
#
|
8 |
+
# The full license is in the file COPYING.txt, distributed with this software.
|
9 |
+
#-----------------------------------------------------------------------------
|
10 |
+
|
11 |
+
#-----------------------------------------------------------------------------
|
12 |
+
# Imports
|
13 |
+
#-----------------------------------------------------------------------------
|
14 |
+
|
15 |
+
# Our own packages
|
16 |
+
from IPython.core.magic import Bunch, Magics, magics_class, line_magic
|
17 |
+
from IPython.testing.skipdoctest import skip_doctest
|
18 |
+
from logging import error
|
19 |
+
|
20 |
+
#-----------------------------------------------------------------------------
|
21 |
+
# Magic implementation classes
|
22 |
+
#-----------------------------------------------------------------------------
|
23 |
+
|
24 |
+
@magics_class
|
25 |
+
class AutoMagics(Magics):
|
26 |
+
"""Magics that control various autoX behaviors."""
|
27 |
+
|
28 |
+
def __init__(self, shell):
|
29 |
+
super(AutoMagics, self).__init__(shell)
|
30 |
+
# namespace for holding state we may need
|
31 |
+
self._magic_state = Bunch()
|
32 |
+
|
33 |
+
@line_magic
|
34 |
+
def automagic(self, parameter_s=''):
|
35 |
+
"""Make magic functions callable without having to type the initial %.
|
36 |
+
|
37 |
+
Without arguments toggles on/off (when off, you must call it as
|
38 |
+
%automagic, of course). With arguments it sets the value, and you can
|
39 |
+
use any of (case insensitive):
|
40 |
+
|
41 |
+
- on, 1, True: to activate
|
42 |
+
|
43 |
+
- off, 0, False: to deactivate.
|
44 |
+
|
45 |
+
Note that magic functions have lowest priority, so if there's a
|
46 |
+
variable whose name collides with that of a magic fn, automagic won't
|
47 |
+
work for that function (you get the variable instead). However, if you
|
48 |
+
delete the variable (del var), the previously shadowed magic function
|
49 |
+
becomes visible to automagic again."""
|
50 |
+
|
51 |
+
arg = parameter_s.lower()
|
52 |
+
mman = self.shell.magics_manager
|
53 |
+
if arg in ('on', '1', 'true'):
|
54 |
+
val = True
|
55 |
+
elif arg in ('off', '0', 'false'):
|
56 |
+
val = False
|
57 |
+
else:
|
58 |
+
val = not mman.auto_magic
|
59 |
+
mman.auto_magic = val
|
60 |
+
print('\n' + self.shell.magics_manager.auto_status())
|
61 |
+
|
62 |
+
@skip_doctest
|
63 |
+
@line_magic
|
64 |
+
def autocall(self, parameter_s=''):
|
65 |
+
"""Make functions callable without having to type parentheses.
|
66 |
+
|
67 |
+
Usage:
|
68 |
+
|
69 |
+
%autocall [mode]
|
70 |
+
|
71 |
+
The mode can be one of: 0->Off, 1->Smart, 2->Full. If not given, the
|
72 |
+
value is toggled on and off (remembering the previous state).
|
73 |
+
|
74 |
+
In more detail, these values mean:
|
75 |
+
|
76 |
+
0 -> fully disabled
|
77 |
+
|
78 |
+
1 -> active, but do not apply if there are no arguments on the line.
|
79 |
+
|
80 |
+
In this mode, you get::
|
81 |
+
|
82 |
+
In [1]: callable
|
83 |
+
Out[1]: <built-in function callable>
|
84 |
+
|
85 |
+
In [2]: callable 'hello'
|
86 |
+
------> callable('hello')
|
87 |
+
Out[2]: False
|
88 |
+
|
89 |
+
2 -> Active always. Even if no arguments are present, the callable
|
90 |
+
object is called::
|
91 |
+
|
92 |
+
In [2]: float
|
93 |
+
------> float()
|
94 |
+
Out[2]: 0.0
|
95 |
+
|
96 |
+
Note that even with autocall off, you can still use '/' at the start of
|
97 |
+
a line to treat the first argument on the command line as a function
|
98 |
+
and add parentheses to it::
|
99 |
+
|
100 |
+
In [8]: /str 43
|
101 |
+
------> str(43)
|
102 |
+
Out[8]: '43'
|
103 |
+
|
104 |
+
# all-random (note for auto-testing)
|
105 |
+
"""
|
106 |
+
|
107 |
+
valid_modes = {
|
108 |
+
0: "Off",
|
109 |
+
1: "Smart",
|
110 |
+
2: "Full",
|
111 |
+
}
|
112 |
+
|
113 |
+
def errorMessage() -> str:
|
114 |
+
error = "Valid modes: "
|
115 |
+
for k, v in valid_modes.items():
|
116 |
+
error += str(k) + "->" + v + ", "
|
117 |
+
error = error[:-2] # remove tailing `, ` after last element
|
118 |
+
return error
|
119 |
+
|
120 |
+
if parameter_s:
|
121 |
+
if parameter_s not in map(str, valid_modes.keys()):
|
122 |
+
error(errorMessage())
|
123 |
+
return
|
124 |
+
arg = int(parameter_s)
|
125 |
+
else:
|
126 |
+
arg = 'toggle'
|
127 |
+
|
128 |
+
if arg not in (*list(valid_modes.keys()), "toggle"):
|
129 |
+
error(errorMessage())
|
130 |
+
return
|
131 |
+
|
132 |
+
if arg in (valid_modes.keys()):
|
133 |
+
self.shell.autocall = arg
|
134 |
+
else: # toggle
|
135 |
+
if self.shell.autocall:
|
136 |
+
self._magic_state.autocall_save = self.shell.autocall
|
137 |
+
self.shell.autocall = 0
|
138 |
+
else:
|
139 |
+
try:
|
140 |
+
self.shell.autocall = self._magic_state.autocall_save
|
141 |
+
except AttributeError:
|
142 |
+
self.shell.autocall = self._magic_state.autocall_save = 1
|
143 |
+
|
144 |
+
print("Automatic calling is:", list(valid_modes.values())[self.shell.autocall])
|
temp_venv/lib/python3.13/site-packages/IPython/core/magics/basic.py
ADDED
@@ -0,0 +1,674 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Implementation of basic magic functions."""
|
2 |
+
|
3 |
+
|
4 |
+
from logging import error
|
5 |
+
import io
|
6 |
+
import os
|
7 |
+
from pprint import pformat
|
8 |
+
import sys
|
9 |
+
from warnings import warn
|
10 |
+
|
11 |
+
from traitlets.utils.importstring import import_item
|
12 |
+
from IPython.core import magic_arguments, page
|
13 |
+
from IPython.core.error import UsageError
|
14 |
+
from IPython.core.magic import Magics, magics_class, line_magic, magic_escapes
|
15 |
+
from IPython.utils.text import format_screen, dedent, indent
|
16 |
+
from IPython.testing.skipdoctest import skip_doctest
|
17 |
+
from IPython.utils.ipstruct import Struct
|
18 |
+
|
19 |
+
|
20 |
+
class MagicsDisplay:
|
21 |
+
def __init__(self, magics_manager, ignore=None):
|
22 |
+
self.ignore = ignore if ignore else []
|
23 |
+
self.magics_manager = magics_manager
|
24 |
+
|
25 |
+
def _lsmagic(self):
|
26 |
+
"""The main implementation of the %lsmagic"""
|
27 |
+
mesc = magic_escapes['line']
|
28 |
+
cesc = magic_escapes['cell']
|
29 |
+
mman = self.magics_manager
|
30 |
+
magics = mman.lsmagic()
|
31 |
+
out = ['Available line magics:',
|
32 |
+
mesc + (' '+mesc).join(sorted([m for m,v in magics['line'].items() if (v not in self.ignore)])),
|
33 |
+
'',
|
34 |
+
'Available cell magics:',
|
35 |
+
cesc + (' '+cesc).join(sorted([m for m,v in magics['cell'].items() if (v not in self.ignore)])),
|
36 |
+
'',
|
37 |
+
mman.auto_status()]
|
38 |
+
return '\n'.join(out)
|
39 |
+
|
40 |
+
def _repr_pretty_(self, p, cycle):
|
41 |
+
p.text(self._lsmagic())
|
42 |
+
|
43 |
+
def __repr__(self):
|
44 |
+
return self.__str__()
|
45 |
+
|
46 |
+
def __str__(self):
|
47 |
+
return self._lsmagic()
|
48 |
+
|
49 |
+
def _jsonable(self):
|
50 |
+
"""turn magics dict into jsonable dict of the same structure
|
51 |
+
|
52 |
+
replaces object instances with their class names as strings
|
53 |
+
"""
|
54 |
+
magic_dict = {}
|
55 |
+
mman = self.magics_manager
|
56 |
+
magics = mman.lsmagic()
|
57 |
+
for key, subdict in magics.items():
|
58 |
+
d = {}
|
59 |
+
magic_dict[key] = d
|
60 |
+
for name, obj in subdict.items():
|
61 |
+
try:
|
62 |
+
classname = obj.__self__.__class__.__name__
|
63 |
+
except AttributeError:
|
64 |
+
classname = 'Other'
|
65 |
+
|
66 |
+
d[name] = classname
|
67 |
+
return magic_dict
|
68 |
+
|
69 |
+
def _repr_json_(self):
|
70 |
+
return self._jsonable()
|
71 |
+
|
72 |
+
|
73 |
+
@magics_class
|
74 |
+
class BasicMagics(Magics):
|
75 |
+
"""Magics that provide central IPython functionality.
|
76 |
+
|
77 |
+
These are various magics that don't fit into specific categories but that
|
78 |
+
are all part of the base 'IPython experience'."""
|
79 |
+
|
80 |
+
@skip_doctest
|
81 |
+
@magic_arguments.magic_arguments()
|
82 |
+
@magic_arguments.argument(
|
83 |
+
'-l', '--line', action='store_true',
|
84 |
+
help="""Create a line magic alias."""
|
85 |
+
)
|
86 |
+
@magic_arguments.argument(
|
87 |
+
'-c', '--cell', action='store_true',
|
88 |
+
help="""Create a cell magic alias."""
|
89 |
+
)
|
90 |
+
@magic_arguments.argument(
|
91 |
+
'name',
|
92 |
+
help="""Name of the magic to be created."""
|
93 |
+
)
|
94 |
+
@magic_arguments.argument(
|
95 |
+
'target',
|
96 |
+
help="""Name of the existing line or cell magic."""
|
97 |
+
)
|
98 |
+
@magic_arguments.argument(
|
99 |
+
'-p', '--params', default=None,
|
100 |
+
help="""Parameters passed to the magic function."""
|
101 |
+
)
|
102 |
+
@line_magic
|
103 |
+
def alias_magic(self, line=''):
|
104 |
+
"""Create an alias for an existing line or cell magic.
|
105 |
+
|
106 |
+
Examples
|
107 |
+
--------
|
108 |
+
::
|
109 |
+
|
110 |
+
In [1]: %alias_magic t timeit
|
111 |
+
Created `%t` as an alias for `%timeit`.
|
112 |
+
Created `%%t` as an alias for `%%timeit`.
|
113 |
+
|
114 |
+
In [2]: %t -n1 pass
|
115 |
+
107 ns ± 43.6 ns per loop (mean ± std. dev. of 7 runs, 1 loop each)
|
116 |
+
|
117 |
+
In [3]: %%t -n1
|
118 |
+
...: pass
|
119 |
+
...:
|
120 |
+
107 ns ± 58.3 ns per loop (mean ± std. dev. of 7 runs, 1 loop each)
|
121 |
+
|
122 |
+
In [4]: %alias_magic --cell whereami pwd
|
123 |
+
UsageError: Cell magic function `%%pwd` not found.
|
124 |
+
In [5]: %alias_magic --line whereami pwd
|
125 |
+
Created `%whereami` as an alias for `%pwd`.
|
126 |
+
|
127 |
+
In [6]: %whereami
|
128 |
+
Out[6]: '/home/testuser'
|
129 |
+
|
130 |
+
In [7]: %alias_magic h history -p "-l 30" --line
|
131 |
+
Created `%h` as an alias for `%history -l 30`.
|
132 |
+
"""
|
133 |
+
|
134 |
+
args = magic_arguments.parse_argstring(self.alias_magic, line)
|
135 |
+
shell = self.shell
|
136 |
+
mman = self.shell.magics_manager
|
137 |
+
escs = ''.join(magic_escapes.values())
|
138 |
+
|
139 |
+
target = args.target.lstrip(escs)
|
140 |
+
name = args.name.lstrip(escs)
|
141 |
+
|
142 |
+
params = args.params
|
143 |
+
if (params and
|
144 |
+
((params.startswith('"') and params.endswith('"'))
|
145 |
+
or (params.startswith("'") and params.endswith("'")))):
|
146 |
+
params = params[1:-1]
|
147 |
+
|
148 |
+
# Find the requested magics.
|
149 |
+
m_line = shell.find_magic(target, 'line')
|
150 |
+
m_cell = shell.find_magic(target, 'cell')
|
151 |
+
if args.line and m_line is None:
|
152 |
+
raise UsageError('Line magic function `%s%s` not found.' %
|
153 |
+
(magic_escapes['line'], target))
|
154 |
+
if args.cell and m_cell is None:
|
155 |
+
raise UsageError('Cell magic function `%s%s` not found.' %
|
156 |
+
(magic_escapes['cell'], target))
|
157 |
+
|
158 |
+
# If --line and --cell are not specified, default to the ones
|
159 |
+
# that are available.
|
160 |
+
if not args.line and not args.cell:
|
161 |
+
if not m_line and not m_cell:
|
162 |
+
raise UsageError(
|
163 |
+
'No line or cell magic with name `%s` found.' % target
|
164 |
+
)
|
165 |
+
args.line = bool(m_line)
|
166 |
+
args.cell = bool(m_cell)
|
167 |
+
|
168 |
+
params_str = "" if params is None else " " + params
|
169 |
+
|
170 |
+
if args.line:
|
171 |
+
mman.register_alias(name, target, 'line', params)
|
172 |
+
print('Created `%s%s` as an alias for `%s%s%s`.' % (
|
173 |
+
magic_escapes['line'], name,
|
174 |
+
magic_escapes['line'], target, params_str))
|
175 |
+
|
176 |
+
if args.cell:
|
177 |
+
mman.register_alias(name, target, 'cell', params)
|
178 |
+
print('Created `%s%s` as an alias for `%s%s%s`.' % (
|
179 |
+
magic_escapes['cell'], name,
|
180 |
+
magic_escapes['cell'], target, params_str))
|
181 |
+
|
182 |
+
@line_magic
|
183 |
+
def lsmagic(self, parameter_s=''):
|
184 |
+
"""List currently available magic functions."""
|
185 |
+
return MagicsDisplay(self.shell.magics_manager, ignore=[])
|
186 |
+
|
187 |
+
def _magic_docs(self, brief=False, rest=False):
|
188 |
+
"""Return docstrings from magic functions."""
|
189 |
+
mman = self.shell.magics_manager
|
190 |
+
docs = mman.lsmagic_docs(brief, missing='No documentation')
|
191 |
+
|
192 |
+
if rest:
|
193 |
+
format_string = '**%s%s**::\n\n%s\n\n'
|
194 |
+
else:
|
195 |
+
format_string = '%s%s:\n%s\n'
|
196 |
+
|
197 |
+
return ''.join(
|
198 |
+
[format_string % (magic_escapes['line'], fname,
|
199 |
+
indent(dedent(fndoc)))
|
200 |
+
for fname, fndoc in sorted(docs['line'].items())]
|
201 |
+
+
|
202 |
+
[format_string % (magic_escapes['cell'], fname,
|
203 |
+
indent(dedent(fndoc)))
|
204 |
+
for fname, fndoc in sorted(docs['cell'].items())]
|
205 |
+
)
|
206 |
+
|
207 |
+
@line_magic
|
208 |
+
def magic(self, parameter_s=''):
|
209 |
+
"""Print information about the magic function system.
|
210 |
+
|
211 |
+
Supported formats: -latex, -brief, -rest
|
212 |
+
"""
|
213 |
+
|
214 |
+
mode = ''
|
215 |
+
try:
|
216 |
+
mode = parameter_s.split()[0][1:]
|
217 |
+
except IndexError:
|
218 |
+
pass
|
219 |
+
|
220 |
+
brief = (mode == 'brief')
|
221 |
+
rest = (mode == 'rest')
|
222 |
+
magic_docs = self._magic_docs(brief, rest)
|
223 |
+
|
224 |
+
if mode == 'latex':
|
225 |
+
print(self.format_latex(magic_docs))
|
226 |
+
return
|
227 |
+
else:
|
228 |
+
magic_docs = format_screen(magic_docs)
|
229 |
+
|
230 |
+
out = ["""
|
231 |
+
IPython's 'magic' functions
|
232 |
+
===========================
|
233 |
+
|
234 |
+
The magic function system provides a series of functions which allow you to
|
235 |
+
control the behavior of IPython itself, plus a lot of system-type
|
236 |
+
features. There are two kinds of magics, line-oriented and cell-oriented.
|
237 |
+
|
238 |
+
Line magics are prefixed with the % character and work much like OS
|
239 |
+
command-line calls: they get as an argument the rest of the line, where
|
240 |
+
arguments are passed without parentheses or quotes. For example, this will
|
241 |
+
time the given statement::
|
242 |
+
|
243 |
+
%timeit range(1000)
|
244 |
+
|
245 |
+
Cell magics are prefixed with a double %%, and they are functions that get as
|
246 |
+
an argument not only the rest of the line, but also the lines below it in a
|
247 |
+
separate argument. These magics are called with two arguments: the rest of the
|
248 |
+
call line and the body of the cell, consisting of the lines below the first.
|
249 |
+
For example::
|
250 |
+
|
251 |
+
%%timeit x = numpy.random.randn((100, 100))
|
252 |
+
numpy.linalg.svd(x)
|
253 |
+
|
254 |
+
will time the execution of the numpy svd routine, running the assignment of x
|
255 |
+
as part of the setup phase, which is not timed.
|
256 |
+
|
257 |
+
In a line-oriented client (the terminal or Qt console IPython), starting a new
|
258 |
+
input with %% will automatically enter cell mode, and IPython will continue
|
259 |
+
reading input until a blank line is given. In the notebook, simply type the
|
260 |
+
whole cell as one entity, but keep in mind that the %% escape can only be at
|
261 |
+
the very start of the cell.
|
262 |
+
|
263 |
+
NOTE: If you have 'automagic' enabled (via the command line option or with the
|
264 |
+
%automagic function), you don't need to type in the % explicitly for line
|
265 |
+
magics; cell magics always require an explicit '%%' escape. By default,
|
266 |
+
IPython ships with automagic on, so you should only rarely need the % escape.
|
267 |
+
|
268 |
+
Example: typing '%cd mydir' (without the quotes) changes your working directory
|
269 |
+
to 'mydir', if it exists.
|
270 |
+
|
271 |
+
For a list of the available magic functions, use %lsmagic. For a description
|
272 |
+
of any of them, type %magic_name?, e.g. '%cd?'.
|
273 |
+
|
274 |
+
Currently the magic system has the following functions:""",
|
275 |
+
magic_docs,
|
276 |
+
"Summary of magic functions (from %slsmagic):" % magic_escapes['line'],
|
277 |
+
str(self.lsmagic()),
|
278 |
+
]
|
279 |
+
page.page('\n'.join(out))
|
280 |
+
|
281 |
+
|
282 |
+
@line_magic
|
283 |
+
def page(self, parameter_s=''):
|
284 |
+
"""Pretty print the object and display it through a pager.
|
285 |
+
|
286 |
+
%page [options] OBJECT
|
287 |
+
|
288 |
+
If no object is given, use _ (last output).
|
289 |
+
|
290 |
+
Options:
|
291 |
+
|
292 |
+
-r: page str(object), don't pretty-print it."""
|
293 |
+
|
294 |
+
# After a function contributed by Olivier Aubert, slightly modified.
|
295 |
+
|
296 |
+
# Process options/args
|
297 |
+
opts, args = self.parse_options(parameter_s, 'r')
|
298 |
+
raw = 'r' in opts
|
299 |
+
|
300 |
+
oname = args and args or '_'
|
301 |
+
info = self.shell._ofind(oname)
|
302 |
+
if info.found:
|
303 |
+
if raw:
|
304 |
+
txt = str(info.obj)
|
305 |
+
else:
|
306 |
+
txt = pformat(info.obj)
|
307 |
+
page.page(txt)
|
308 |
+
else:
|
309 |
+
print('Object `%s` not found' % oname)
|
310 |
+
|
311 |
+
@line_magic
|
312 |
+
def pprint(self, parameter_s=''):
|
313 |
+
"""Toggle pretty printing on/off."""
|
314 |
+
ptformatter = self.shell.display_formatter.formatters['text/plain']
|
315 |
+
ptformatter.pprint = bool(1 - ptformatter.pprint)
|
316 |
+
print('Pretty printing has been turned',
|
317 |
+
['OFF','ON'][ptformatter.pprint])
|
318 |
+
|
319 |
+
@line_magic
|
320 |
+
def colors(self, parameter_s=''):
|
321 |
+
"""Switch color scheme/theme globally for IPython
|
322 |
+
|
323 |
+
Examples
|
324 |
+
--------
|
325 |
+
To get a plain black and white terminal::
|
326 |
+
|
327 |
+
%colors nocolor
|
328 |
+
"""
|
329 |
+
|
330 |
+
|
331 |
+
new_theme = parameter_s.strip()
|
332 |
+
if not new_theme:
|
333 |
+
from IPython.utils.PyColorize import theme_table
|
334 |
+
|
335 |
+
raise UsageError(
|
336 |
+
"%colors: you must specify a color theme. See '%colors?'."
|
337 |
+
f" Available themes: {list(theme_table.keys())}"
|
338 |
+
)
|
339 |
+
|
340 |
+
self.shell.colors = new_theme
|
341 |
+
|
342 |
+
@line_magic
|
343 |
+
def xmode(self, parameter_s=''):
|
344 |
+
"""Switch modes for the exception handlers.
|
345 |
+
|
346 |
+
Valid modes: Plain, Context, Verbose, and Minimal.
|
347 |
+
|
348 |
+
If called without arguments, acts as a toggle.
|
349 |
+
|
350 |
+
When in verbose mode the value `--show` (and `--hide`)
|
351 |
+
will respectively show (or hide) frames with ``__tracebackhide__ =
|
352 |
+
True`` value set.
|
353 |
+
"""
|
354 |
+
|
355 |
+
def xmode_switch_err(name):
|
356 |
+
warn('Error changing %s exception modes.\n%s' %
|
357 |
+
(name,sys.exc_info()[1]))
|
358 |
+
|
359 |
+
shell = self.shell
|
360 |
+
if parameter_s.strip() == "--show":
|
361 |
+
shell.InteractiveTB.skip_hidden = False
|
362 |
+
return
|
363 |
+
if parameter_s.strip() == "--hide":
|
364 |
+
shell.InteractiveTB.skip_hidden = True
|
365 |
+
return
|
366 |
+
|
367 |
+
new_mode = parameter_s.strip().capitalize()
|
368 |
+
try:
|
369 |
+
shell.InteractiveTB.set_mode(mode=new_mode)
|
370 |
+
print('Exception reporting mode:',shell.InteractiveTB.mode)
|
371 |
+
except:
|
372 |
+
raise
|
373 |
+
xmode_switch_err('user')
|
374 |
+
|
375 |
+
@line_magic
|
376 |
+
def quickref(self, arg):
|
377 |
+
""" Show a quick reference sheet """
|
378 |
+
from IPython.core.usage import quick_reference
|
379 |
+
qr = quick_reference + self._magic_docs(brief=True)
|
380 |
+
page.page(qr)
|
381 |
+
|
382 |
+
@line_magic
|
383 |
+
def doctest_mode(self, parameter_s=''):
|
384 |
+
"""Toggle doctest mode on and off.
|
385 |
+
|
386 |
+
This mode is intended to make IPython behave as much as possible like a
|
387 |
+
plain Python shell, from the perspective of how its prompts, exceptions
|
388 |
+
and output look. This makes it easy to copy and paste parts of a
|
389 |
+
session into doctests. It does so by:
|
390 |
+
|
391 |
+
- Changing the prompts to the classic ``>>>`` ones.
|
392 |
+
- Changing the exception reporting mode to 'Plain'.
|
393 |
+
- Disabling pretty-printing of output.
|
394 |
+
|
395 |
+
Note that IPython also supports the pasting of code snippets that have
|
396 |
+
leading '>>>' and '...' prompts in them. This means that you can paste
|
397 |
+
doctests from files or docstrings (even if they have leading
|
398 |
+
whitespace), and the code will execute correctly. You can then use
|
399 |
+
'%history -t' to see the translated history; this will give you the
|
400 |
+
input after removal of all the leading prompts and whitespace, which
|
401 |
+
can be pasted back into an editor.
|
402 |
+
|
403 |
+
With these features, you can switch into this mode easily whenever you
|
404 |
+
need to do testing and changes to doctests, without having to leave
|
405 |
+
your existing IPython session.
|
406 |
+
"""
|
407 |
+
|
408 |
+
# Shorthands
|
409 |
+
shell = self.shell
|
410 |
+
meta = shell.meta
|
411 |
+
disp_formatter = self.shell.display_formatter
|
412 |
+
ptformatter = disp_formatter.formatters['text/plain']
|
413 |
+
# dstore is a data store kept in the instance metadata bag to track any
|
414 |
+
# changes we make, so we can undo them later.
|
415 |
+
dstore = meta.setdefault('doctest_mode',Struct())
|
416 |
+
save_dstore = dstore.setdefault
|
417 |
+
|
418 |
+
# save a few values we'll need to recover later
|
419 |
+
mode = save_dstore('mode',False)
|
420 |
+
save_dstore('rc_pprint',ptformatter.pprint)
|
421 |
+
save_dstore('xmode',shell.InteractiveTB.mode)
|
422 |
+
save_dstore('rc_separate_out',shell.separate_out)
|
423 |
+
save_dstore('rc_separate_out2',shell.separate_out2)
|
424 |
+
save_dstore('rc_separate_in',shell.separate_in)
|
425 |
+
save_dstore('rc_active_types',disp_formatter.active_types)
|
426 |
+
|
427 |
+
if not mode:
|
428 |
+
# turn on
|
429 |
+
|
430 |
+
# Prompt separators like plain python
|
431 |
+
shell.separate_in = ''
|
432 |
+
shell.separate_out = ''
|
433 |
+
shell.separate_out2 = ''
|
434 |
+
|
435 |
+
|
436 |
+
ptformatter.pprint = False
|
437 |
+
disp_formatter.active_types = ['text/plain']
|
438 |
+
|
439 |
+
shell.run_line_magic("xmode", "Plain")
|
440 |
+
else:
|
441 |
+
# turn off
|
442 |
+
shell.separate_in = dstore.rc_separate_in
|
443 |
+
|
444 |
+
shell.separate_out = dstore.rc_separate_out
|
445 |
+
shell.separate_out2 = dstore.rc_separate_out2
|
446 |
+
|
447 |
+
ptformatter.pprint = dstore.rc_pprint
|
448 |
+
disp_formatter.active_types = dstore.rc_active_types
|
449 |
+
|
450 |
+
shell.run_line_magic("xmode", dstore.xmode)
|
451 |
+
|
452 |
+
# mode here is the state before we switch; switch_doctest_mode takes
|
453 |
+
# the mode we're switching to.
|
454 |
+
shell.switch_doctest_mode(not mode)
|
455 |
+
|
456 |
+
# Store new mode and inform
|
457 |
+
dstore.mode = bool(not mode)
|
458 |
+
mode_label = ['OFF','ON'][dstore.mode]
|
459 |
+
print('Doctest mode is:', mode_label)
|
460 |
+
|
461 |
+
@line_magic
|
462 |
+
def gui(self, parameter_s=''):
|
463 |
+
"""Enable or disable IPython GUI event loop integration.
|
464 |
+
|
465 |
+
%gui [GUINAME]
|
466 |
+
|
467 |
+
This magic replaces IPython's threaded shells that were activated
|
468 |
+
using the (pylab/wthread/etc.) command line flags. GUI toolkits
|
469 |
+
can now be enabled at runtime and keyboard
|
470 |
+
interrupts should work without any problems. The following toolkits
|
471 |
+
are supported: wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX)::
|
472 |
+
|
473 |
+
%gui wx # enable wxPython event loop integration
|
474 |
+
%gui qt # enable PyQt/PySide event loop integration
|
475 |
+
# with the latest version available.
|
476 |
+
%gui qt6 # enable PyQt6/PySide6 event loop integration
|
477 |
+
%gui qt5 # enable PyQt5/PySide2 event loop integration
|
478 |
+
%gui gtk # enable PyGTK event loop integration
|
479 |
+
%gui gtk3 # enable Gtk3 event loop integration
|
480 |
+
%gui gtk4 # enable Gtk4 event loop integration
|
481 |
+
%gui tk # enable Tk event loop integration
|
482 |
+
%gui osx # enable Cocoa event loop integration
|
483 |
+
# (requires %matplotlib 1.1)
|
484 |
+
%gui # disable all event loop integration
|
485 |
+
|
486 |
+
WARNING: after any of these has been called you can simply create
|
487 |
+
an application object, but DO NOT start the event loop yourself, as
|
488 |
+
we have already handled that.
|
489 |
+
"""
|
490 |
+
opts, arg = self.parse_options(parameter_s, '')
|
491 |
+
if arg=='': arg = None
|
492 |
+
try:
|
493 |
+
return self.shell.enable_gui(arg)
|
494 |
+
except Exception as e:
|
495 |
+
# print simple error message, rather than traceback if we can't
|
496 |
+
# hook up the GUI
|
497 |
+
error(str(e))
|
498 |
+
|
499 |
+
@skip_doctest
|
500 |
+
@line_magic
|
501 |
+
def precision(self, s=''):
|
502 |
+
"""Set floating point precision for pretty printing.
|
503 |
+
|
504 |
+
Can set either integer precision or a format string.
|
505 |
+
|
506 |
+
If numpy has been imported and precision is an int,
|
507 |
+
numpy display precision will also be set, via ``numpy.set_printoptions``.
|
508 |
+
|
509 |
+
If no argument is given, defaults will be restored.
|
510 |
+
|
511 |
+
Examples
|
512 |
+
--------
|
513 |
+
::
|
514 |
+
|
515 |
+
In [1]: from math import pi
|
516 |
+
|
517 |
+
In [2]: %precision 3
|
518 |
+
Out[2]: '%.3f'
|
519 |
+
|
520 |
+
In [3]: pi
|
521 |
+
Out[3]: 3.142
|
522 |
+
|
523 |
+
In [4]: %precision %i
|
524 |
+
Out[4]: '%i'
|
525 |
+
|
526 |
+
In [5]: pi
|
527 |
+
Out[5]: 3
|
528 |
+
|
529 |
+
In [6]: %precision %e
|
530 |
+
Out[6]: '%e'
|
531 |
+
|
532 |
+
In [7]: pi**10
|
533 |
+
Out[7]: 9.364805e+04
|
534 |
+
|
535 |
+
In [8]: %precision
|
536 |
+
Out[8]: '%r'
|
537 |
+
|
538 |
+
In [9]: pi**10
|
539 |
+
Out[9]: 93648.047476082982
|
540 |
+
"""
|
541 |
+
ptformatter = self.shell.display_formatter.formatters['text/plain']
|
542 |
+
ptformatter.float_precision = s
|
543 |
+
return ptformatter.float_format
|
544 |
+
|
545 |
+
@magic_arguments.magic_arguments()
|
546 |
+
@magic_arguments.argument(
|
547 |
+
'filename', type=str,
|
548 |
+
help='Notebook name or filename'
|
549 |
+
)
|
550 |
+
@line_magic
|
551 |
+
def notebook(self, s):
|
552 |
+
"""Export and convert IPython notebooks.
|
553 |
+
|
554 |
+
This function can export the current IPython history to a notebook file.
|
555 |
+
For example, to export the history to "foo.ipynb" do "%notebook foo.ipynb".
|
556 |
+
"""
|
557 |
+
args = magic_arguments.parse_argstring(self.notebook, s)
|
558 |
+
outfname = os.path.expanduser(args.filename)
|
559 |
+
|
560 |
+
from nbformat import write, v4
|
561 |
+
|
562 |
+
cells = []
|
563 |
+
hist = list(self.shell.history_manager.get_range())
|
564 |
+
outputs = self.shell.history_manager.outputs
|
565 |
+
exceptions = self.shell.history_manager.exceptions
|
566 |
+
|
567 |
+
if(len(hist)<=1):
|
568 |
+
raise ValueError('History is empty, cannot export')
|
569 |
+
for session, execution_count, source in hist[:-1]:
|
570 |
+
cell = v4.new_code_cell(execution_count=execution_count, source=source)
|
571 |
+
for output in outputs[execution_count]:
|
572 |
+
for mime_type, data in output.bundle.items():
|
573 |
+
if output.output_type == "out_stream":
|
574 |
+
cell.outputs.append(v4.new_output("stream", text=[data]))
|
575 |
+
elif output.output_type == "err_stream":
|
576 |
+
err_output = v4.new_output("stream", text=[data])
|
577 |
+
err_output.name = "stderr"
|
578 |
+
cell.outputs.append(err_output)
|
579 |
+
elif output.output_type == "execute_result":
|
580 |
+
cell.outputs.append(
|
581 |
+
v4.new_output(
|
582 |
+
"execute_result",
|
583 |
+
data={mime_type: data},
|
584 |
+
execution_count=execution_count,
|
585 |
+
)
|
586 |
+
)
|
587 |
+
elif output.output_type == "display_data":
|
588 |
+
cell.outputs.append(
|
589 |
+
v4.new_output(
|
590 |
+
"display_data",
|
591 |
+
data={mime_type: data},
|
592 |
+
)
|
593 |
+
)
|
594 |
+
else:
|
595 |
+
raise ValueError(f"Unknown output type: {output.output_type}")
|
596 |
+
|
597 |
+
# Check if this execution_count is in exceptions (current session)
|
598 |
+
if execution_count in exceptions:
|
599 |
+
cell.outputs.append(
|
600 |
+
v4.new_output("error", **exceptions[execution_count])
|
601 |
+
)
|
602 |
+
cells.append(cell)
|
603 |
+
|
604 |
+
nb = v4.new_notebook(cells=cells)
|
605 |
+
with io.open(outfname, "w", encoding="utf-8") as f:
|
606 |
+
write(nb, f, version=4)
|
607 |
+
|
608 |
+
@magics_class
|
609 |
+
class AsyncMagics(BasicMagics):
|
610 |
+
|
611 |
+
@line_magic
|
612 |
+
def autoawait(self, parameter_s):
|
613 |
+
"""
|
614 |
+
Allow to change the status of the autoawait option.
|
615 |
+
|
616 |
+
This allow you to set a specific asynchronous code runner.
|
617 |
+
|
618 |
+
If no value is passed, print the currently used asynchronous integration
|
619 |
+
and whether it is activated.
|
620 |
+
|
621 |
+
It can take a number of value evaluated in the following order:
|
622 |
+
|
623 |
+
- False/false/off deactivate autoawait integration
|
624 |
+
- True/true/on activate autoawait integration using configured default
|
625 |
+
loop
|
626 |
+
- asyncio/curio/trio activate autoawait integration and use integration
|
627 |
+
with said library.
|
628 |
+
|
629 |
+
- `sync` turn on the pseudo-sync integration (mostly used for
|
630 |
+
`IPython.embed()` which does not run IPython with a real eventloop and
|
631 |
+
deactivate running asynchronous code. Turning on Asynchronous code with
|
632 |
+
the pseudo sync loop is undefined behavior and may lead IPython to crash.
|
633 |
+
|
634 |
+
If the passed parameter does not match any of the above and is a python
|
635 |
+
identifier, get said object from user namespace and set it as the
|
636 |
+
runner, and activate autoawait.
|
637 |
+
|
638 |
+
If the object is a fully qualified object name, attempt to import it and
|
639 |
+
set it as the runner, and activate autoawait.
|
640 |
+
|
641 |
+
The exact behavior of autoawait is experimental and subject to change
|
642 |
+
across version of IPython and Python.
|
643 |
+
"""
|
644 |
+
|
645 |
+
param = parameter_s.strip()
|
646 |
+
d = {True: "on", False: "off"}
|
647 |
+
|
648 |
+
if not param:
|
649 |
+
print("IPython autoawait is `{}`, and set to use `{}`".format(
|
650 |
+
d[self.shell.autoawait],
|
651 |
+
self.shell.loop_runner
|
652 |
+
))
|
653 |
+
return None
|
654 |
+
|
655 |
+
if param.lower() in ('false', 'off'):
|
656 |
+
self.shell.autoawait = False
|
657 |
+
return None
|
658 |
+
if param.lower() in ('true', 'on'):
|
659 |
+
self.shell.autoawait = True
|
660 |
+
return None
|
661 |
+
|
662 |
+
if param in self.shell.loop_runner_map:
|
663 |
+
self.shell.loop_runner, self.shell.autoawait = self.shell.loop_runner_map[param]
|
664 |
+
return None
|
665 |
+
|
666 |
+
if param in self.shell.user_ns :
|
667 |
+
self.shell.loop_runner = self.shell.user_ns[param]
|
668 |
+
self.shell.autoawait = True
|
669 |
+
return None
|
670 |
+
|
671 |
+
runner = import_item(param)
|
672 |
+
|
673 |
+
self.shell.loop_runner = runner
|
674 |
+
self.shell.autoawait = True
|
temp_venv/lib/python3.13/site-packages/IPython/core/magics/code.py
ADDED
@@ -0,0 +1,757 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Implementation of code management magic functions.
|
2 |
+
"""
|
3 |
+
#-----------------------------------------------------------------------------
|
4 |
+
# Copyright (c) 2012 The IPython Development Team.
|
5 |
+
#
|
6 |
+
# Distributed under the terms of the Modified BSD License.
|
7 |
+
#
|
8 |
+
# The full license is in the file COPYING.txt, distributed with this software.
|
9 |
+
#-----------------------------------------------------------------------------
|
10 |
+
|
11 |
+
#-----------------------------------------------------------------------------
|
12 |
+
# Imports
|
13 |
+
#-----------------------------------------------------------------------------
|
14 |
+
|
15 |
+
# Stdlib
|
16 |
+
import inspect
|
17 |
+
import io
|
18 |
+
import os
|
19 |
+
import re
|
20 |
+
import sys
|
21 |
+
import ast
|
22 |
+
from itertools import chain
|
23 |
+
from urllib.request import Request, urlopen
|
24 |
+
from urllib.parse import urlencode
|
25 |
+
from pathlib import Path
|
26 |
+
|
27 |
+
# Our own packages
|
28 |
+
from IPython.core.error import TryNext, StdinNotImplementedError, UsageError
|
29 |
+
from IPython.core.macro import Macro
|
30 |
+
from IPython.core.magic import Magics, magics_class, line_magic
|
31 |
+
from IPython.core.oinspect import find_file, find_source_lines
|
32 |
+
from IPython.core.release import version
|
33 |
+
from IPython.testing.skipdoctest import skip_doctest
|
34 |
+
from IPython.utils.contexts import preserve_keys
|
35 |
+
from IPython.utils.path import get_py_filename
|
36 |
+
from warnings import warn
|
37 |
+
from logging import error
|
38 |
+
from IPython.utils.text import get_text_list
|
39 |
+
|
40 |
+
#-----------------------------------------------------------------------------
|
41 |
+
# Magic implementation classes
|
42 |
+
#-----------------------------------------------------------------------------
|
43 |
+
|
44 |
+
# Used for exception handling in magic_edit
|
45 |
+
class MacroToEdit(ValueError): pass
|
46 |
+
|
47 |
+
ipython_input_pat = re.compile(r"<ipython\-input\-(\d+)-[a-z\d]+>$")
|
48 |
+
|
49 |
+
# To match, e.g. 8-10 1:5 :10 3-
|
50 |
+
range_re = re.compile(r"""
|
51 |
+
(?P<start>\d+)?
|
52 |
+
((?P<sep>[\-:])
|
53 |
+
(?P<end>\d+)?)?
|
54 |
+
$""", re.VERBOSE)
|
55 |
+
|
56 |
+
|
57 |
+
def extract_code_ranges(ranges_str):
|
58 |
+
"""Turn a string of range for %%load into 2-tuples of (start, stop)
|
59 |
+
ready to use as a slice of the content split by lines.
|
60 |
+
|
61 |
+
Examples
|
62 |
+
--------
|
63 |
+
list(extract_input_ranges("5-10 2"))
|
64 |
+
[(4, 10), (1, 2)]
|
65 |
+
"""
|
66 |
+
for range_str in ranges_str.split():
|
67 |
+
rmatch = range_re.match(range_str)
|
68 |
+
if not rmatch:
|
69 |
+
continue
|
70 |
+
sep = rmatch.group("sep")
|
71 |
+
start = rmatch.group("start")
|
72 |
+
end = rmatch.group("end")
|
73 |
+
|
74 |
+
if sep == '-':
|
75 |
+
start = int(start) - 1 if start else None
|
76 |
+
end = int(end) if end else None
|
77 |
+
elif sep == ':':
|
78 |
+
start = int(start) - 1 if start else None
|
79 |
+
end = int(end) - 1 if end else None
|
80 |
+
else:
|
81 |
+
end = int(start)
|
82 |
+
start = int(start) - 1
|
83 |
+
yield (start, end)
|
84 |
+
|
85 |
+
|
86 |
+
def extract_symbols(code, symbols):
|
87 |
+
"""
|
88 |
+
Return a tuple (blocks, not_found)
|
89 |
+
where ``blocks`` is a list of code fragments
|
90 |
+
for each symbol parsed from code, and ``not_found`` are
|
91 |
+
symbols not found in the code.
|
92 |
+
|
93 |
+
For example::
|
94 |
+
|
95 |
+
In [1]: code = '''a = 10
|
96 |
+
...: def b(): return 42
|
97 |
+
...: class A: pass'''
|
98 |
+
|
99 |
+
In [2]: extract_symbols(code, 'A,b,z')
|
100 |
+
Out[2]: (['class A: pass\\n', 'def b(): return 42\\n'], ['z'])
|
101 |
+
"""
|
102 |
+
symbols = symbols.split(',')
|
103 |
+
|
104 |
+
# this will raise SyntaxError if code isn't valid Python
|
105 |
+
py_code = ast.parse(code)
|
106 |
+
|
107 |
+
marks = [(getattr(s, 'name', None), s.lineno) for s in py_code.body]
|
108 |
+
code = code.split('\n')
|
109 |
+
|
110 |
+
symbols_lines = {}
|
111 |
+
|
112 |
+
# we already know the start_lineno of each symbol (marks).
|
113 |
+
# To find each end_lineno, we traverse in reverse order until each
|
114 |
+
# non-blank line
|
115 |
+
end = len(code)
|
116 |
+
for name, start in reversed(marks):
|
117 |
+
while not code[end - 1].strip():
|
118 |
+
end -= 1
|
119 |
+
if name:
|
120 |
+
symbols_lines[name] = (start - 1, end)
|
121 |
+
end = start - 1
|
122 |
+
|
123 |
+
# Now symbols_lines is a map
|
124 |
+
# {'symbol_name': (start_lineno, end_lineno), ...}
|
125 |
+
|
126 |
+
# fill a list with chunks of codes for each requested symbol
|
127 |
+
blocks = []
|
128 |
+
not_found = []
|
129 |
+
for symbol in symbols:
|
130 |
+
if symbol in symbols_lines:
|
131 |
+
start, end = symbols_lines[symbol]
|
132 |
+
blocks.append('\n'.join(code[start:end]) + '\n')
|
133 |
+
else:
|
134 |
+
not_found.append(symbol)
|
135 |
+
|
136 |
+
return blocks, not_found
|
137 |
+
|
138 |
+
def strip_initial_indent(lines):
|
139 |
+
"""For %load, strip indent from lines until finding an unindented line.
|
140 |
+
|
141 |
+
https://github.com/ipython/ipython/issues/9775
|
142 |
+
"""
|
143 |
+
indent_re = re.compile(r'\s+')
|
144 |
+
|
145 |
+
it = iter(lines)
|
146 |
+
first_line = next(it)
|
147 |
+
indent_match = indent_re.match(first_line)
|
148 |
+
|
149 |
+
if indent_match:
|
150 |
+
# First line was indented
|
151 |
+
indent = indent_match.group()
|
152 |
+
yield first_line[len(indent):]
|
153 |
+
|
154 |
+
for line in it:
|
155 |
+
if line.startswith(indent):
|
156 |
+
yield line[len(indent) :]
|
157 |
+
elif line in ("\n", "\r\n") or len(line) == 0:
|
158 |
+
yield line
|
159 |
+
else:
|
160 |
+
# Less indented than the first line - stop dedenting
|
161 |
+
yield line
|
162 |
+
break
|
163 |
+
else:
|
164 |
+
yield first_line
|
165 |
+
|
166 |
+
# Pass the remaining lines through without dedenting
|
167 |
+
for line in it:
|
168 |
+
yield line
|
169 |
+
|
170 |
+
|
171 |
+
class InteractivelyDefined(Exception):
|
172 |
+
"""Exception for interactively defined variable in magic_edit"""
|
173 |
+
def __init__(self, index):
|
174 |
+
self.index = index
|
175 |
+
|
176 |
+
|
177 |
+
@magics_class
|
178 |
+
class CodeMagics(Magics):
|
179 |
+
"""Magics related to code management (loading, saving, editing, ...)."""
|
180 |
+
|
181 |
+
def __init__(self, *args, **kwargs):
|
182 |
+
self._knowntemps = set()
|
183 |
+
super(CodeMagics, self).__init__(*args, **kwargs)
|
184 |
+
|
185 |
+
@line_magic
|
186 |
+
def save(self, parameter_s=''):
|
187 |
+
"""Save a set of lines or a macro to a given filename.
|
188 |
+
|
189 |
+
Usage:\\
|
190 |
+
%save [options] filename [history]
|
191 |
+
|
192 |
+
Options:
|
193 |
+
|
194 |
+
-r: use 'raw' input. By default, the 'processed' history is used,
|
195 |
+
so that magics are loaded in their transformed version to valid
|
196 |
+
Python. If this option is given, the raw input as typed at the
|
197 |
+
command line is used instead.
|
198 |
+
|
199 |
+
-f: force overwrite. If file exists, %save will prompt for overwrite
|
200 |
+
unless -f is given.
|
201 |
+
|
202 |
+
-a: append to the file instead of overwriting it.
|
203 |
+
|
204 |
+
The history argument uses the same syntax as %history for input ranges,
|
205 |
+
then saves the lines to the filename you specify.
|
206 |
+
|
207 |
+
If no ranges are specified, saves history of the current session up to
|
208 |
+
this point.
|
209 |
+
|
210 |
+
It adds a '.py' extension to the file if you don't do so yourself, and
|
211 |
+
it asks for confirmation before overwriting existing files.
|
212 |
+
|
213 |
+
If `-r` option is used, the default extension is `.ipy`.
|
214 |
+
"""
|
215 |
+
|
216 |
+
opts,args = self.parse_options(parameter_s,'fra',mode='list')
|
217 |
+
if not args:
|
218 |
+
raise UsageError('Missing filename.')
|
219 |
+
raw = 'r' in opts
|
220 |
+
force = 'f' in opts
|
221 |
+
append = 'a' in opts
|
222 |
+
mode = 'a' if append else 'w'
|
223 |
+
ext = '.ipy' if raw else '.py'
|
224 |
+
fname, codefrom = args[0], " ".join(args[1:])
|
225 |
+
if not fname.endswith(('.py','.ipy')):
|
226 |
+
fname += ext
|
227 |
+
fname = os.path.expanduser(fname)
|
228 |
+
file_exists = os.path.isfile(fname)
|
229 |
+
if file_exists and not force and not append:
|
230 |
+
try:
|
231 |
+
overwrite = self.shell.ask_yes_no('File `%s` exists. Overwrite (y/[N])? ' % fname, default='n')
|
232 |
+
except StdinNotImplementedError:
|
233 |
+
print("File `%s` exists. Use `%%save -f %s` to force overwrite" % (fname, parameter_s))
|
234 |
+
return
|
235 |
+
if not overwrite :
|
236 |
+
print('Operation cancelled.')
|
237 |
+
return
|
238 |
+
try:
|
239 |
+
cmds = self.shell.find_user_code(codefrom,raw)
|
240 |
+
except (TypeError, ValueError) as e:
|
241 |
+
print(e.args[0])
|
242 |
+
return
|
243 |
+
with io.open(fname, mode, encoding="utf-8") as f:
|
244 |
+
if not file_exists or not append:
|
245 |
+
f.write("# coding: utf-8\n")
|
246 |
+
f.write(cmds)
|
247 |
+
# make sure we end on a newline
|
248 |
+
if not cmds.endswith('\n'):
|
249 |
+
f.write('\n')
|
250 |
+
print('The following commands were written to file `%s`:' % fname)
|
251 |
+
print(cmds)
|
252 |
+
|
253 |
+
@line_magic
|
254 |
+
def pastebin(self, parameter_s=''):
|
255 |
+
"""Upload code to dpaste.com, returning the URL.
|
256 |
+
|
257 |
+
Usage:\\
|
258 |
+
%pastebin [-d "Custom description"][-e 24] 1-7
|
259 |
+
|
260 |
+
The argument can be an input history range, a filename, or the name of a
|
261 |
+
string or macro.
|
262 |
+
|
263 |
+
If no arguments are given, uploads the history of this session up to
|
264 |
+
this point.
|
265 |
+
|
266 |
+
Options:
|
267 |
+
|
268 |
+
-d: Pass a custom description. The default will say
|
269 |
+
"Pasted from IPython".
|
270 |
+
-e: Pass number of days for the link to be expired.
|
271 |
+
The default will be 7 days.
|
272 |
+
"""
|
273 |
+
opts, args = self.parse_options(parameter_s, "d:e:")
|
274 |
+
|
275 |
+
try:
|
276 |
+
code = self.shell.find_user_code(args)
|
277 |
+
except (ValueError, TypeError) as e:
|
278 |
+
print(e.args[0])
|
279 |
+
return
|
280 |
+
|
281 |
+
expiry_days = 7
|
282 |
+
try:
|
283 |
+
expiry_days = int(opts.get("e", 7))
|
284 |
+
except ValueError as e:
|
285 |
+
print(e.args[0].capitalize())
|
286 |
+
return
|
287 |
+
if expiry_days < 1 or expiry_days > 365:
|
288 |
+
print("Expiry days should be in range of 1 to 365")
|
289 |
+
return
|
290 |
+
|
291 |
+
post_data = urlencode(
|
292 |
+
{
|
293 |
+
"title": opts.get("d", "Pasted from IPython"),
|
294 |
+
"syntax": "python",
|
295 |
+
"content": code,
|
296 |
+
"expiry_days": expiry_days,
|
297 |
+
}
|
298 |
+
).encode("utf-8")
|
299 |
+
|
300 |
+
request = Request(
|
301 |
+
"https://dpaste.com/api/v2/",
|
302 |
+
headers={"User-Agent": "IPython v{}".format(version)},
|
303 |
+
)
|
304 |
+
response = urlopen(request, post_data)
|
305 |
+
return response.headers.get('Location')
|
306 |
+
|
307 |
+
@line_magic
|
308 |
+
def loadpy(self, arg_s):
|
309 |
+
"""Alias of `%load`
|
310 |
+
|
311 |
+
`%loadpy` has gained some flexibility and dropped the requirement of a `.py`
|
312 |
+
extension. So it has been renamed simply into %load. You can look at
|
313 |
+
`%load`'s docstring for more info.
|
314 |
+
"""
|
315 |
+
self.load(arg_s)
|
316 |
+
|
317 |
+
@line_magic
|
318 |
+
def load(self, arg_s):
|
319 |
+
"""Load code into the current frontend.
|
320 |
+
|
321 |
+
Usage:\\
|
322 |
+
%load [options] source
|
323 |
+
|
324 |
+
where source can be a filename, URL, input history range, macro, or
|
325 |
+
element in the user namespace
|
326 |
+
|
327 |
+
If no arguments are given, loads the history of this session up to this
|
328 |
+
point.
|
329 |
+
|
330 |
+
Options:
|
331 |
+
|
332 |
+
-r <lines>: Specify lines or ranges of lines to load from the source.
|
333 |
+
Ranges could be specified as x-y (x..y) or in python-style x:y
|
334 |
+
(x..(y-1)). Both limits x and y can be left blank (meaning the
|
335 |
+
beginning and end of the file, respectively).
|
336 |
+
|
337 |
+
-s <symbols>: Specify function or classes to load from python source.
|
338 |
+
|
339 |
+
-y : Don't ask confirmation for loading source above 200 000 characters.
|
340 |
+
|
341 |
+
-n : Include the user's namespace when searching for source code.
|
342 |
+
|
343 |
+
This magic command can either take a local filename, a URL, an history
|
344 |
+
range (see %history) or a macro as argument, it will prompt for
|
345 |
+
confirmation before loading source with more than 200 000 characters, unless
|
346 |
+
-y flag is passed or if the frontend does not support raw_input::
|
347 |
+
|
348 |
+
%load
|
349 |
+
%load myscript.py
|
350 |
+
%load 7-27
|
351 |
+
%load myMacro
|
352 |
+
%load http://www.example.com/myscript.py
|
353 |
+
%load -r 5-10 myscript.py
|
354 |
+
%load -r 10-20,30,40: foo.py
|
355 |
+
%load -s MyClass,wonder_function myscript.py
|
356 |
+
%load -n MyClass
|
357 |
+
%load -n my_module.wonder_function
|
358 |
+
"""
|
359 |
+
opts,args = self.parse_options(arg_s,'yns:r:')
|
360 |
+
search_ns = 'n' in opts
|
361 |
+
contents = self.shell.find_user_code(args, search_ns=search_ns)
|
362 |
+
|
363 |
+
if 's' in opts:
|
364 |
+
try:
|
365 |
+
blocks, not_found = extract_symbols(contents, opts['s'])
|
366 |
+
except SyntaxError:
|
367 |
+
# non python code
|
368 |
+
error("Unable to parse the input as valid Python code")
|
369 |
+
return
|
370 |
+
|
371 |
+
if len(not_found) == 1:
|
372 |
+
warn('The symbol `%s` was not found' % not_found[0])
|
373 |
+
elif len(not_found) > 1:
|
374 |
+
warn('The symbols %s were not found' % get_text_list(not_found,
|
375 |
+
wrap_item_with='`')
|
376 |
+
)
|
377 |
+
|
378 |
+
contents = '\n'.join(blocks)
|
379 |
+
|
380 |
+
if 'r' in opts:
|
381 |
+
ranges = opts['r'].replace(',', ' ')
|
382 |
+
lines = contents.split('\n')
|
383 |
+
slices = extract_code_ranges(ranges)
|
384 |
+
contents = [lines[slice(*slc)] for slc in slices]
|
385 |
+
contents = '\n'.join(strip_initial_indent(chain.from_iterable(contents)))
|
386 |
+
|
387 |
+
l = len(contents)
|
388 |
+
|
389 |
+
# 200 000 is ~ 2500 full 80 character lines
|
390 |
+
# so in average, more than 5000 lines
|
391 |
+
if l > 200000 and 'y' not in opts:
|
392 |
+
try:
|
393 |
+
ans = self.shell.ask_yes_no(("The text you're trying to load seems pretty big"\
|
394 |
+
" (%d characters). Continue (y/[N]) ?" % l), default='n' )
|
395 |
+
except StdinNotImplementedError:
|
396 |
+
#assume yes if raw input not implemented
|
397 |
+
ans = True
|
398 |
+
|
399 |
+
if ans is False :
|
400 |
+
print('Operation cancelled.')
|
401 |
+
return
|
402 |
+
|
403 |
+
contents = "# %load {}\n".format(arg_s) + contents
|
404 |
+
|
405 |
+
self.shell.set_next_input(contents, replace=True)
|
406 |
+
|
407 |
+
@staticmethod
|
408 |
+
def _find_edit_target(shell, args, opts, last_call):
|
409 |
+
"""Utility method used by magic_edit to find what to edit."""
|
410 |
+
|
411 |
+
def make_filename(arg):
|
412 |
+
"Make a filename from the given args"
|
413 |
+
try:
|
414 |
+
filename = get_py_filename(arg)
|
415 |
+
except IOError:
|
416 |
+
# If it ends with .py but doesn't already exist, assume we want
|
417 |
+
# a new file.
|
418 |
+
if arg.endswith('.py'):
|
419 |
+
filename = arg
|
420 |
+
else:
|
421 |
+
filename = None
|
422 |
+
return filename
|
423 |
+
|
424 |
+
# Set a few locals from the options for convenience:
|
425 |
+
opts_prev = 'p' in opts
|
426 |
+
opts_raw = 'r' in opts
|
427 |
+
|
428 |
+
# custom exceptions
|
429 |
+
class DataIsObject(Exception): pass
|
430 |
+
|
431 |
+
# Default line number value
|
432 |
+
lineno = opts.get('n',None)
|
433 |
+
|
434 |
+
if opts_prev:
|
435 |
+
args = '_%s' % last_call[0]
|
436 |
+
if args not in shell.user_ns:
|
437 |
+
args = last_call[1]
|
438 |
+
|
439 |
+
# by default this is done with temp files, except when the given
|
440 |
+
# arg is a filename
|
441 |
+
use_temp = True
|
442 |
+
|
443 |
+
data = ''
|
444 |
+
|
445 |
+
# First, see if the arguments should be a filename.
|
446 |
+
filename = make_filename(args)
|
447 |
+
if filename:
|
448 |
+
use_temp = False
|
449 |
+
elif args:
|
450 |
+
# Mode where user specifies ranges of lines, like in %macro.
|
451 |
+
data = shell.extract_input_lines(args, opts_raw)
|
452 |
+
if not data:
|
453 |
+
try:
|
454 |
+
# Load the parameter given as a variable. If not a string,
|
455 |
+
# process it as an object instead (below)
|
456 |
+
|
457 |
+
# print('*** args',args,'type',type(args)) # dbg
|
458 |
+
data = eval(args, shell.user_ns)
|
459 |
+
if not isinstance(data, str):
|
460 |
+
raise DataIsObject
|
461 |
+
|
462 |
+
except (NameError,SyntaxError):
|
463 |
+
# given argument is not a variable, try as a filename
|
464 |
+
filename = make_filename(args)
|
465 |
+
if filename is None:
|
466 |
+
warn("Argument given (%s) can't be found as a variable "
|
467 |
+
"or as a filename." % args)
|
468 |
+
return (None, None, None)
|
469 |
+
use_temp = False
|
470 |
+
|
471 |
+
except DataIsObject as e:
|
472 |
+
# macros have a special edit function
|
473 |
+
if isinstance(data, Macro):
|
474 |
+
raise MacroToEdit(data) from e
|
475 |
+
|
476 |
+
# For objects, try to edit the file where they are defined
|
477 |
+
filename = find_file(data)
|
478 |
+
if filename:
|
479 |
+
if 'fakemodule' in filename.lower() and \
|
480 |
+
inspect.isclass(data):
|
481 |
+
# class created by %edit? Try to find source
|
482 |
+
# by looking for method definitions instead, the
|
483 |
+
# __module__ in those classes is FakeModule.
|
484 |
+
attrs = [getattr(data, aname) for aname in dir(data)]
|
485 |
+
for attr in attrs:
|
486 |
+
if not inspect.ismethod(attr):
|
487 |
+
continue
|
488 |
+
filename = find_file(attr)
|
489 |
+
if filename and \
|
490 |
+
'fakemodule' not in filename.lower():
|
491 |
+
# change the attribute to be the edit
|
492 |
+
# target instead
|
493 |
+
data = attr
|
494 |
+
break
|
495 |
+
|
496 |
+
m = ipython_input_pat.match(os.path.basename(filename))
|
497 |
+
if m:
|
498 |
+
raise InteractivelyDefined(int(m.groups()[0])) from e
|
499 |
+
|
500 |
+
datafile = 1
|
501 |
+
if filename is None:
|
502 |
+
filename = make_filename(args)
|
503 |
+
datafile = 1
|
504 |
+
if filename is not None:
|
505 |
+
# only warn about this if we get a real name
|
506 |
+
warn('Could not find file where `%s` is defined.\n'
|
507 |
+
'Opening a file named `%s`' % (args, filename))
|
508 |
+
# Now, make sure we can actually read the source (if it was
|
509 |
+
# in a temp file it's gone by now).
|
510 |
+
if datafile:
|
511 |
+
if lineno is None:
|
512 |
+
lineno = find_source_lines(data)
|
513 |
+
if lineno is None:
|
514 |
+
filename = make_filename(args)
|
515 |
+
if filename is None:
|
516 |
+
warn('The file where `%s` was defined '
|
517 |
+
'cannot be read or found.' % data)
|
518 |
+
return (None, None, None)
|
519 |
+
use_temp = False
|
520 |
+
|
521 |
+
if use_temp:
|
522 |
+
filename = shell.mktempfile(data)
|
523 |
+
print('IPython will make a temporary file named:',filename)
|
524 |
+
|
525 |
+
# use last_call to remember the state of the previous call, but don't
|
526 |
+
# let it be clobbered by successive '-p' calls.
|
527 |
+
try:
|
528 |
+
last_call[0] = shell.displayhook.prompt_count
|
529 |
+
if not opts_prev:
|
530 |
+
last_call[1] = args
|
531 |
+
except:
|
532 |
+
pass
|
533 |
+
|
534 |
+
|
535 |
+
return filename, lineno, use_temp
|
536 |
+
|
537 |
+
def _edit_macro(self,mname,macro):
|
538 |
+
"""open an editor with the macro data in a file"""
|
539 |
+
filename = self.shell.mktempfile(macro.value)
|
540 |
+
self.shell.hooks.editor(filename)
|
541 |
+
|
542 |
+
# and make a new macro object, to replace the old one
|
543 |
+
mvalue = Path(filename).read_text(encoding="utf-8")
|
544 |
+
self.shell.user_ns[mname] = Macro(mvalue)
|
545 |
+
|
546 |
+
@skip_doctest
|
547 |
+
@line_magic
|
548 |
+
def edit(self, parameter_s='',last_call=['','']):
|
549 |
+
"""Bring up an editor and execute the resulting code.
|
550 |
+
|
551 |
+
Usage:
|
552 |
+
%edit [options] [args]
|
553 |
+
|
554 |
+
%edit runs IPython's editor hook. The default version of this hook is
|
555 |
+
set to call the editor specified by your $EDITOR environment variable.
|
556 |
+
If this isn't found, it will default to vi under Linux/Unix and to
|
557 |
+
notepad under Windows. See the end of this docstring for how to change
|
558 |
+
the editor hook.
|
559 |
+
|
560 |
+
You can also set the value of this editor via the
|
561 |
+
``TerminalInteractiveShell.editor`` option in your configuration file.
|
562 |
+
This is useful if you wish to use a different editor from your typical
|
563 |
+
default with IPython (and for Windows users who typically don't set
|
564 |
+
environment variables).
|
565 |
+
|
566 |
+
This command allows you to conveniently edit multi-line code right in
|
567 |
+
your IPython session.
|
568 |
+
|
569 |
+
If called without arguments, %edit opens up an empty editor with a
|
570 |
+
temporary file and will execute the contents of this file when you
|
571 |
+
close it (don't forget to save it!).
|
572 |
+
|
573 |
+
|
574 |
+
Options:
|
575 |
+
|
576 |
+
-n <number>: open the editor at a specified line number. By default,
|
577 |
+
the IPython editor hook uses the unix syntax 'editor +N filename', but
|
578 |
+
you can configure this by providing your own modified hook if your
|
579 |
+
favorite editor supports line-number specifications with a different
|
580 |
+
syntax.
|
581 |
+
|
582 |
+
-p: this will call the editor with the same data as the previous time
|
583 |
+
it was used, regardless of how long ago (in your current session) it
|
584 |
+
was.
|
585 |
+
|
586 |
+
-r: use 'raw' input. This option only applies to input taken from the
|
587 |
+
user's history. By default, the 'processed' history is used, so that
|
588 |
+
magics are loaded in their transformed version to valid Python. If
|
589 |
+
this option is given, the raw input as typed as the command line is
|
590 |
+
used instead. When you exit the editor, it will be executed by
|
591 |
+
IPython's own processor.
|
592 |
+
|
593 |
+
-x: do not execute the edited code immediately upon exit. This is
|
594 |
+
mainly useful if you are editing programs which need to be called with
|
595 |
+
command line arguments, which you can then do using %run.
|
596 |
+
|
597 |
+
|
598 |
+
Arguments:
|
599 |
+
|
600 |
+
If arguments are given, the following possibilities exist:
|
601 |
+
|
602 |
+
- If the argument is a filename, IPython will load that into the
|
603 |
+
editor. It will execute its contents with execfile() when you exit,
|
604 |
+
loading any code in the file into your interactive namespace.
|
605 |
+
|
606 |
+
- The arguments are ranges of input history, e.g. "7 ~1/4-6".
|
607 |
+
The syntax is the same as in the %history magic.
|
608 |
+
|
609 |
+
- If the argument is a string variable, its contents are loaded
|
610 |
+
into the editor. You can thus edit any string which contains
|
611 |
+
python code (including the result of previous edits).
|
612 |
+
|
613 |
+
- If the argument is the name of an object (other than a string),
|
614 |
+
IPython will try to locate the file where it was defined and open the
|
615 |
+
editor at the point where it is defined. You can use `%edit function`
|
616 |
+
to load an editor exactly at the point where 'function' is defined,
|
617 |
+
edit it and have the file be executed automatically.
|
618 |
+
|
619 |
+
- If the object is a macro (see %macro for details), this opens up your
|
620 |
+
specified editor with a temporary file containing the macro's data.
|
621 |
+
Upon exit, the macro is reloaded with the contents of the file.
|
622 |
+
|
623 |
+
Note: opening at an exact line is only supported under Unix, and some
|
624 |
+
editors (like kedit and gedit up to Gnome 2.8) do not understand the
|
625 |
+
'+NUMBER' parameter necessary for this feature. Good editors like
|
626 |
+
(X)Emacs, vi, jed, pico and joe all do.
|
627 |
+
|
628 |
+
After executing your code, %edit will return as output the code you
|
629 |
+
typed in the editor (except when it was an existing file). This way
|
630 |
+
you can reload the code in further invocations of %edit as a variable,
|
631 |
+
via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
|
632 |
+
the output.
|
633 |
+
|
634 |
+
Note that %edit is also available through the alias %ed.
|
635 |
+
|
636 |
+
This is an example of creating a simple function inside the editor and
|
637 |
+
then modifying it. First, start up the editor::
|
638 |
+
|
639 |
+
In [1]: edit
|
640 |
+
Editing... done. Executing edited code...
|
641 |
+
Out[1]: 'def foo():\\n print("foo() was defined in an editing
|
642 |
+
session")\\n'
|
643 |
+
|
644 |
+
We can then call the function foo()::
|
645 |
+
|
646 |
+
In [2]: foo()
|
647 |
+
foo() was defined in an editing session
|
648 |
+
|
649 |
+
Now we edit foo. IPython automatically loads the editor with the
|
650 |
+
(temporary) file where foo() was previously defined::
|
651 |
+
|
652 |
+
In [3]: edit foo
|
653 |
+
Editing... done. Executing edited code...
|
654 |
+
|
655 |
+
And if we call foo() again we get the modified version::
|
656 |
+
|
657 |
+
In [4]: foo()
|
658 |
+
foo() has now been changed!
|
659 |
+
|
660 |
+
Here is an example of how to edit a code snippet successive
|
661 |
+
times. First we call the editor::
|
662 |
+
|
663 |
+
In [5]: edit
|
664 |
+
Editing... done. Executing edited code...
|
665 |
+
hello
|
666 |
+
Out[5]: "print('hello')\\n"
|
667 |
+
|
668 |
+
Now we call it again with the previous output (stored in _)::
|
669 |
+
|
670 |
+
In [6]: edit _
|
671 |
+
Editing... done. Executing edited code...
|
672 |
+
hello world
|
673 |
+
Out[6]: "print('hello world')\\n"
|
674 |
+
|
675 |
+
Now we call it with the output #8 (stored in _8, also as Out[8])::
|
676 |
+
|
677 |
+
In [7]: edit _8
|
678 |
+
Editing... done. Executing edited code...
|
679 |
+
hello again
|
680 |
+
Out[7]: "print('hello again')\\n"
|
681 |
+
|
682 |
+
|
683 |
+
Changing the default editor hook:
|
684 |
+
|
685 |
+
If you wish to write your own editor hook, you can put it in a
|
686 |
+
configuration file which you load at startup time. The default hook
|
687 |
+
is defined in the IPython.core.hooks module, and you can use that as a
|
688 |
+
starting example for further modifications. That file also has
|
689 |
+
general instructions on how to set a new hook for use once you've
|
690 |
+
defined it."""
|
691 |
+
opts,args = self.parse_options(parameter_s,'prxn:')
|
692 |
+
|
693 |
+
try:
|
694 |
+
filename, lineno, is_temp = self._find_edit_target(self.shell,
|
695 |
+
args, opts, last_call)
|
696 |
+
except MacroToEdit as e:
|
697 |
+
self._edit_macro(args, e.args[0])
|
698 |
+
return
|
699 |
+
except InteractivelyDefined as e:
|
700 |
+
print("Editing In[%i]" % e.index)
|
701 |
+
args = str(e.index)
|
702 |
+
filename, lineno, is_temp = self._find_edit_target(self.shell,
|
703 |
+
args, opts, last_call)
|
704 |
+
if filename is None:
|
705 |
+
# nothing was found, warnings have already been issued,
|
706 |
+
# just give up.
|
707 |
+
return
|
708 |
+
|
709 |
+
if is_temp:
|
710 |
+
self._knowntemps.add(filename)
|
711 |
+
elif (filename in self._knowntemps):
|
712 |
+
is_temp = True
|
713 |
+
|
714 |
+
|
715 |
+
# do actual editing here
|
716 |
+
print('Editing...', end=' ')
|
717 |
+
sys.stdout.flush()
|
718 |
+
filepath = Path(filename)
|
719 |
+
try:
|
720 |
+
# Quote filenames that may have spaces in them when opening
|
721 |
+
# the editor
|
722 |
+
quoted = filename = str(filepath.absolute())
|
723 |
+
if " " in quoted:
|
724 |
+
quoted = "'%s'" % quoted
|
725 |
+
self.shell.hooks.editor(quoted, lineno)
|
726 |
+
except TryNext:
|
727 |
+
warn('Could not open editor')
|
728 |
+
return
|
729 |
+
|
730 |
+
# XXX TODO: should this be generalized for all string vars?
|
731 |
+
# For now, this is special-cased to blocks created by cpaste
|
732 |
+
if args.strip() == "pasted_block":
|
733 |
+
self.shell.user_ns["pasted_block"] = filepath.read_text(encoding="utf-8")
|
734 |
+
|
735 |
+
if 'x' in opts: # -x prevents actual execution
|
736 |
+
print()
|
737 |
+
else:
|
738 |
+
print('done. Executing edited code...')
|
739 |
+
with preserve_keys(self.shell.user_ns, '__file__'):
|
740 |
+
if not is_temp:
|
741 |
+
self.shell.user_ns["__file__"] = filename
|
742 |
+
if "r" in opts: # Untranslated IPython code
|
743 |
+
source = filepath.read_text(encoding="utf-8")
|
744 |
+
self.shell.run_cell(source, store_history=False)
|
745 |
+
else:
|
746 |
+
self.shell.safe_execfile(filename, self.shell.user_ns,
|
747 |
+
self.shell.user_ns)
|
748 |
+
|
749 |
+
if is_temp:
|
750 |
+
try:
|
751 |
+
return filepath.read_text(encoding="utf-8")
|
752 |
+
except IOError as msg:
|
753 |
+
if Path(msg.filename) == filepath:
|
754 |
+
warn('File not found. Did you forget to save?')
|
755 |
+
return
|
756 |
+
else:
|
757 |
+
self.shell.showtraceback()
|
temp_venv/lib/python3.13/site-packages/IPython/core/magics/config.py
ADDED
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Implementation of configuration-related magic functions.
|
2 |
+
"""
|
3 |
+
#-----------------------------------------------------------------------------
|
4 |
+
# Copyright (c) 2012 The IPython Development Team.
|
5 |
+
#
|
6 |
+
# Distributed under the terms of the Modified BSD License.
|
7 |
+
#
|
8 |
+
# The full license is in the file COPYING.txt, distributed with this software.
|
9 |
+
#-----------------------------------------------------------------------------
|
10 |
+
|
11 |
+
#-----------------------------------------------------------------------------
|
12 |
+
# Imports
|
13 |
+
#-----------------------------------------------------------------------------
|
14 |
+
|
15 |
+
# Stdlib
|
16 |
+
import re
|
17 |
+
|
18 |
+
# Our own packages
|
19 |
+
from IPython.core.error import UsageError
|
20 |
+
from IPython.core.magic import Magics, magics_class, line_magic
|
21 |
+
from logging import error
|
22 |
+
|
23 |
+
#-----------------------------------------------------------------------------
|
24 |
+
# Magic implementation classes
|
25 |
+
#-----------------------------------------------------------------------------
|
26 |
+
|
27 |
+
reg = re.compile(r'^\w+\.\w+$')
|
28 |
+
@magics_class
|
29 |
+
class ConfigMagics(Magics):
|
30 |
+
|
31 |
+
def __init__(self, shell):
|
32 |
+
super(ConfigMagics, self).__init__(shell)
|
33 |
+
self.configurables = []
|
34 |
+
|
35 |
+
@line_magic
|
36 |
+
def config(self, s):
|
37 |
+
"""configure IPython
|
38 |
+
|
39 |
+
%config Class[.trait=value]
|
40 |
+
|
41 |
+
This magic exposes most of the IPython config system. Any
|
42 |
+
Configurable class should be able to be configured with the simple
|
43 |
+
line::
|
44 |
+
|
45 |
+
%config Class.trait=value
|
46 |
+
|
47 |
+
Where `value` will be resolved in the user's namespace, if it is an
|
48 |
+
expression or variable name.
|
49 |
+
|
50 |
+
Examples
|
51 |
+
--------
|
52 |
+
|
53 |
+
To see what classes are available for config, pass no arguments::
|
54 |
+
|
55 |
+
In [1]: %config
|
56 |
+
Available objects for config:
|
57 |
+
AliasManager
|
58 |
+
DisplayFormatter
|
59 |
+
HistoryManager
|
60 |
+
IPCompleter
|
61 |
+
LoggingMagics
|
62 |
+
MagicsManager
|
63 |
+
OSMagics
|
64 |
+
PrefilterManager
|
65 |
+
ScriptMagics
|
66 |
+
TerminalInteractiveShell
|
67 |
+
|
68 |
+
To view what is configurable on a given class, just pass the class
|
69 |
+
name::
|
70 |
+
|
71 |
+
In [2]: %config LoggingMagics
|
72 |
+
LoggingMagics(Magics) options
|
73 |
+
---------------------------
|
74 |
+
LoggingMagics.quiet=<Bool>
|
75 |
+
Suppress output of log state when logging is enabled
|
76 |
+
Current: False
|
77 |
+
|
78 |
+
but the real use is in setting values::
|
79 |
+
|
80 |
+
In [3]: %config LoggingMagics.quiet = True
|
81 |
+
|
82 |
+
and these values are read from the user_ns if they are variables::
|
83 |
+
|
84 |
+
In [4]: feeling_quiet=False
|
85 |
+
|
86 |
+
In [5]: %config LoggingMagics.quiet = feeling_quiet
|
87 |
+
|
88 |
+
"""
|
89 |
+
from traitlets.config.loader import Config
|
90 |
+
# some IPython objects are Configurable, but do not yet have
|
91 |
+
# any configurable traits. Exclude them from the effects of
|
92 |
+
# this magic, as their presence is just noise:
|
93 |
+
configurables = sorted(set([ c for c in self.shell.configurables
|
94 |
+
if c.__class__.class_traits(config=True)
|
95 |
+
]), key=lambda x: x.__class__.__name__)
|
96 |
+
classnames = [ c.__class__.__name__ for c in configurables ]
|
97 |
+
|
98 |
+
line = s.strip()
|
99 |
+
if not line:
|
100 |
+
# print available configurable names
|
101 |
+
print("Available objects for config:")
|
102 |
+
for name in classnames:
|
103 |
+
print(" ", name)
|
104 |
+
return
|
105 |
+
elif line in classnames:
|
106 |
+
# `%config TerminalInteractiveShell` will print trait info for
|
107 |
+
# TerminalInteractiveShell
|
108 |
+
c = configurables[classnames.index(line)]
|
109 |
+
cls = c.__class__
|
110 |
+
help = cls.class_get_help(c)
|
111 |
+
# strip leading '--' from cl-args:
|
112 |
+
help = re.sub(re.compile(r'^--', re.MULTILINE), '', help)
|
113 |
+
print(help)
|
114 |
+
return
|
115 |
+
elif reg.match(line):
|
116 |
+
cls, attr = line.split('.')
|
117 |
+
return getattr(configurables[classnames.index(cls)],attr)
|
118 |
+
elif '=' not in line:
|
119 |
+
msg = "Invalid config statement: %r, "\
|
120 |
+
"should be `Class.trait = value`."
|
121 |
+
|
122 |
+
ll = line.lower()
|
123 |
+
for classname in classnames:
|
124 |
+
if ll == classname.lower():
|
125 |
+
msg = msg + '\nDid you mean %s (note the case)?' % classname
|
126 |
+
break
|
127 |
+
|
128 |
+
raise UsageError( msg % line)
|
129 |
+
|
130 |
+
# otherwise, assume we are setting configurables.
|
131 |
+
# leave quotes on args when splitting, because we want
|
132 |
+
# unquoted args to eval in user_ns
|
133 |
+
cfg = Config()
|
134 |
+
exec("cfg."+line, self.shell.user_ns, locals())
|
135 |
+
|
136 |
+
for configurable in configurables:
|
137 |
+
try:
|
138 |
+
configurable.update_config(cfg)
|
139 |
+
except Exception as e:
|
140 |
+
error(e)
|
temp_venv/lib/python3.13/site-packages/IPython/core/magics/display.py
ADDED
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Simple magics for display formats"""
|
2 |
+
#-----------------------------------------------------------------------------
|
3 |
+
# Copyright (c) 2012 The IPython Development Team.
|
4 |
+
#
|
5 |
+
# Distributed under the terms of the Modified BSD License.
|
6 |
+
#
|
7 |
+
# The full license is in the file COPYING.txt, distributed with this software.
|
8 |
+
#-----------------------------------------------------------------------------
|
9 |
+
|
10 |
+
#-----------------------------------------------------------------------------
|
11 |
+
# Imports
|
12 |
+
#-----------------------------------------------------------------------------
|
13 |
+
|
14 |
+
# Our own packages
|
15 |
+
from IPython.display import display, Javascript, Latex, SVG, HTML, Markdown
|
16 |
+
from IPython.core.magic import (
|
17 |
+
Magics, magics_class, cell_magic
|
18 |
+
)
|
19 |
+
from IPython.core import magic_arguments
|
20 |
+
|
21 |
+
#-----------------------------------------------------------------------------
|
22 |
+
# Magic implementation classes
|
23 |
+
#-----------------------------------------------------------------------------
|
24 |
+
|
25 |
+
|
26 |
+
@magics_class
|
27 |
+
class DisplayMagics(Magics):
|
28 |
+
"""Magics for displaying various output types with literals
|
29 |
+
|
30 |
+
Defines javascript/latex/svg/html cell magics for writing
|
31 |
+
blocks in those languages, to be rendered in the frontend.
|
32 |
+
"""
|
33 |
+
|
34 |
+
@cell_magic
|
35 |
+
def js(self, line, cell):
|
36 |
+
"""Run the cell block of Javascript code
|
37 |
+
|
38 |
+
Alias of `%%javascript`
|
39 |
+
|
40 |
+
Starting with IPython 8.0 %%javascript is pending deprecation to be replaced
|
41 |
+
by a more flexible system
|
42 |
+
|
43 |
+
Please See https://github.com/ipython/ipython/issues/13376
|
44 |
+
"""
|
45 |
+
self.javascript(line, cell)
|
46 |
+
|
47 |
+
@cell_magic
|
48 |
+
def javascript(self, line, cell):
|
49 |
+
"""Run the cell block of Javascript code
|
50 |
+
|
51 |
+
Starting with IPython 8.0 %%javascript is pending deprecation to be replaced
|
52 |
+
by a more flexible system
|
53 |
+
|
54 |
+
Please See https://github.com/ipython/ipython/issues/13376
|
55 |
+
"""
|
56 |
+
display(Javascript(cell))
|
57 |
+
|
58 |
+
|
59 |
+
@cell_magic
|
60 |
+
def latex(self, line, cell):
|
61 |
+
"""Render the cell as a block of LaTeX
|
62 |
+
|
63 |
+
The subset of LaTeX which is supported depends on the implementation in
|
64 |
+
the client. In the Jupyter Notebook, this magic only renders the subset
|
65 |
+
of LaTeX defined by MathJax
|
66 |
+
[here](https://docs.mathjax.org/en/v2.5-latest/tex.html)."""
|
67 |
+
display(Latex(cell))
|
68 |
+
|
69 |
+
@cell_magic
|
70 |
+
def svg(self, line, cell):
|
71 |
+
"""Render the cell as an SVG literal"""
|
72 |
+
display(SVG(cell))
|
73 |
+
|
74 |
+
@magic_arguments.magic_arguments()
|
75 |
+
@magic_arguments.argument(
|
76 |
+
'--isolated', action='store_true', default=False,
|
77 |
+
help="""Annotate the cell as 'isolated'.
|
78 |
+
Isolated cells are rendered inside their own <iframe> tag"""
|
79 |
+
)
|
80 |
+
@cell_magic
|
81 |
+
def html(self, line, cell):
|
82 |
+
"""Render the cell as a block of HTML"""
|
83 |
+
args = magic_arguments.parse_argstring(self.html, line)
|
84 |
+
html = HTML(cell)
|
85 |
+
if args.isolated:
|
86 |
+
display(html, metadata={'text/html':{'isolated':True}})
|
87 |
+
else:
|
88 |
+
display(html)
|
89 |
+
|
90 |
+
@cell_magic
|
91 |
+
def markdown(self, line, cell):
|
92 |
+
"""Render the cell as Markdown text block"""
|
93 |
+
display(Markdown(cell))
|
temp_venv/lib/python3.13/site-packages/IPython/core/magics/execution.py
ADDED
@@ -0,0 +1,1677 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# -*- coding: utf-8 -*-
|
2 |
+
"""Implementation of execution-related magic functions."""
|
3 |
+
|
4 |
+
# Copyright (c) IPython Development Team.
|
5 |
+
# Distributed under the terms of the Modified BSD License.
|
6 |
+
|
7 |
+
|
8 |
+
import ast
|
9 |
+
import bdb
|
10 |
+
import builtins as builtin_mod
|
11 |
+
import copy
|
12 |
+
import cProfile as profile
|
13 |
+
import gc
|
14 |
+
import itertools
|
15 |
+
import math
|
16 |
+
import os
|
17 |
+
import pstats
|
18 |
+
import re
|
19 |
+
import shlex
|
20 |
+
import sys
|
21 |
+
import time
|
22 |
+
import timeit
|
23 |
+
from typing import Dict, Any
|
24 |
+
from ast import (
|
25 |
+
Assign,
|
26 |
+
Call,
|
27 |
+
Expr,
|
28 |
+
Load,
|
29 |
+
Module,
|
30 |
+
Name,
|
31 |
+
NodeTransformer,
|
32 |
+
Store,
|
33 |
+
parse,
|
34 |
+
unparse,
|
35 |
+
)
|
36 |
+
from io import StringIO
|
37 |
+
from logging import error
|
38 |
+
from pathlib import Path
|
39 |
+
from pdb import Restart
|
40 |
+
from textwrap import dedent, indent
|
41 |
+
from warnings import warn
|
42 |
+
|
43 |
+
from IPython.core import magic_arguments, oinspect, page
|
44 |
+
from IPython.core.displayhook import DisplayHook
|
45 |
+
from IPython.core.error import UsageError
|
46 |
+
from IPython.core.macro import Macro
|
47 |
+
from IPython.core.magic import (
|
48 |
+
Magics,
|
49 |
+
cell_magic,
|
50 |
+
line_cell_magic,
|
51 |
+
line_magic,
|
52 |
+
magics_class,
|
53 |
+
needs_local_scope,
|
54 |
+
no_var_expand,
|
55 |
+
on_off,
|
56 |
+
output_can_be_silenced,
|
57 |
+
)
|
58 |
+
from IPython.testing.skipdoctest import skip_doctest
|
59 |
+
from IPython.utils.capture import capture_output
|
60 |
+
from IPython.utils.contexts import preserve_keys
|
61 |
+
from IPython.utils.ipstruct import Struct
|
62 |
+
from IPython.utils.module_paths import find_mod
|
63 |
+
from IPython.utils.path import get_py_filename, shellglob
|
64 |
+
from IPython.utils.timing import clock, clock2
|
65 |
+
from IPython.core.magics.ast_mod import ReplaceCodeTransformer
|
66 |
+
|
67 |
+
#-----------------------------------------------------------------------------
|
68 |
+
# Magic implementation classes
|
69 |
+
#-----------------------------------------------------------------------------
|
70 |
+
|
71 |
+
|
72 |
+
class TimeitResult:
|
73 |
+
"""
|
74 |
+
Object returned by the timeit magic with info about the run.
|
75 |
+
|
76 |
+
Contains the following attributes:
|
77 |
+
|
78 |
+
loops: int
|
79 |
+
number of loops done per measurement
|
80 |
+
|
81 |
+
repeat: int
|
82 |
+
number of times the measurement was repeated
|
83 |
+
|
84 |
+
best: float
|
85 |
+
best execution time / number
|
86 |
+
|
87 |
+
all_runs : list[float]
|
88 |
+
execution time of each run (in s)
|
89 |
+
|
90 |
+
compile_time: float
|
91 |
+
time of statement compilation (s)
|
92 |
+
|
93 |
+
"""
|
94 |
+
def __init__(self, loops, repeat, best, worst, all_runs, compile_time, precision):
|
95 |
+
self.loops = loops
|
96 |
+
self.repeat = repeat
|
97 |
+
self.best = best
|
98 |
+
self.worst = worst
|
99 |
+
self.all_runs = all_runs
|
100 |
+
self.compile_time = compile_time
|
101 |
+
self._precision = precision
|
102 |
+
self.timings = [dt / self.loops for dt in all_runs]
|
103 |
+
|
104 |
+
@property
|
105 |
+
def average(self):
|
106 |
+
return math.fsum(self.timings) / len(self.timings)
|
107 |
+
|
108 |
+
@property
|
109 |
+
def stdev(self):
|
110 |
+
mean = self.average
|
111 |
+
return (math.fsum([(x - mean) ** 2 for x in self.timings]) / len(self.timings)) ** 0.5
|
112 |
+
|
113 |
+
def __str__(self):
|
114 |
+
pm = '+-'
|
115 |
+
if hasattr(sys.stdout, 'encoding') and sys.stdout.encoding:
|
116 |
+
try:
|
117 |
+
"\xb1".encode(sys.stdout.encoding)
|
118 |
+
pm = "\xb1"
|
119 |
+
except:
|
120 |
+
pass
|
121 |
+
return "{mean} {pm} {std} per loop (mean {pm} std. dev. of {runs} run{run_plural}, {loops:,} loop{loop_plural} each)".format(
|
122 |
+
pm=pm,
|
123 |
+
runs=self.repeat,
|
124 |
+
loops=self.loops,
|
125 |
+
loop_plural="" if self.loops == 1 else "s",
|
126 |
+
run_plural="" if self.repeat == 1 else "s",
|
127 |
+
mean=_format_time(self.average, self._precision),
|
128 |
+
std=_format_time(self.stdev, self._precision),
|
129 |
+
)
|
130 |
+
|
131 |
+
def _repr_pretty_(self, p , cycle):
|
132 |
+
unic = self.__str__()
|
133 |
+
p.text("<TimeitResult : " + unic + ">")
|
134 |
+
|
135 |
+
|
136 |
+
class TimeitTemplateFiller(ast.NodeTransformer):
|
137 |
+
"""Fill in the AST template for timing execution.
|
138 |
+
|
139 |
+
This is quite closely tied to the template definition, which is in
|
140 |
+
:meth:`ExecutionMagics.timeit`.
|
141 |
+
"""
|
142 |
+
def __init__(self, ast_setup, ast_stmt):
|
143 |
+
self.ast_setup = ast_setup
|
144 |
+
self.ast_stmt = ast_stmt
|
145 |
+
|
146 |
+
def visit_FunctionDef(self, node):
|
147 |
+
"Fill in the setup statement"
|
148 |
+
self.generic_visit(node)
|
149 |
+
if node.name == "inner":
|
150 |
+
node.body[:1] = self.ast_setup.body
|
151 |
+
|
152 |
+
return node
|
153 |
+
|
154 |
+
def visit_For(self, node):
|
155 |
+
"Fill in the statement to be timed"
|
156 |
+
if getattr(getattr(node.body[0], 'value', None), 'id', None) == 'stmt':
|
157 |
+
node.body = self.ast_stmt.body
|
158 |
+
return node
|
159 |
+
|
160 |
+
|
161 |
+
class Timer(timeit.Timer):
|
162 |
+
"""Timer class that explicitly uses self.inner
|
163 |
+
|
164 |
+
which is an undocumented implementation detail of CPython,
|
165 |
+
not shared by PyPy.
|
166 |
+
"""
|
167 |
+
|
168 |
+
# Timer.timeit copied from CPython 3.4.2
|
169 |
+
def timeit(self, number=timeit.default_number):
|
170 |
+
"""Time 'number' executions of the main statement.
|
171 |
+
|
172 |
+
To be precise, this executes the setup statement once, and
|
173 |
+
then returns the time it takes to execute the main statement
|
174 |
+
a number of times, as a float measured in seconds. The
|
175 |
+
argument is the number of times through the loop, defaulting
|
176 |
+
to one million. The main statement, the setup statement and
|
177 |
+
the timer function to be used are passed to the constructor.
|
178 |
+
"""
|
179 |
+
it = itertools.repeat(None, number)
|
180 |
+
gcold = gc.isenabled()
|
181 |
+
gc.disable()
|
182 |
+
try:
|
183 |
+
timing = self.inner(it, self.timer)
|
184 |
+
finally:
|
185 |
+
if gcold:
|
186 |
+
gc.enable()
|
187 |
+
return timing
|
188 |
+
|
189 |
+
|
190 |
+
@magics_class
|
191 |
+
class ExecutionMagics(Magics):
|
192 |
+
"""Magics related to code execution, debugging, profiling, etc."""
|
193 |
+
|
194 |
+
_transformers: Dict[str, Any] = {}
|
195 |
+
|
196 |
+
def __init__(self, shell):
|
197 |
+
super(ExecutionMagics, self).__init__(shell)
|
198 |
+
# Default execution function used to actually run user code.
|
199 |
+
self.default_runner = None
|
200 |
+
|
201 |
+
@skip_doctest
|
202 |
+
@no_var_expand
|
203 |
+
@line_cell_magic
|
204 |
+
def prun(self, parameter_s='', cell=None):
|
205 |
+
"""Run a statement through the python code profiler.
|
206 |
+
|
207 |
+
**Usage, in line mode**::
|
208 |
+
|
209 |
+
%prun [options] statement
|
210 |
+
|
211 |
+
**Usage, in cell mode**::
|
212 |
+
|
213 |
+
%%prun [options] [statement]
|
214 |
+
code...
|
215 |
+
code...
|
216 |
+
|
217 |
+
In cell mode, the additional code lines are appended to the (possibly
|
218 |
+
empty) statement in the first line. Cell mode allows you to easily
|
219 |
+
profile multiline blocks without having to put them in a separate
|
220 |
+
function.
|
221 |
+
|
222 |
+
The given statement (which doesn't require quote marks) is run via the
|
223 |
+
python profiler in a manner similar to the profile.run() function.
|
224 |
+
Namespaces are internally managed to work correctly; profile.run
|
225 |
+
cannot be used in IPython because it makes certain assumptions about
|
226 |
+
namespaces which do not hold under IPython.
|
227 |
+
|
228 |
+
Options:
|
229 |
+
|
230 |
+
-l <limit>
|
231 |
+
you can place restrictions on what or how much of the
|
232 |
+
profile gets printed. The limit value can be:
|
233 |
+
|
234 |
+
* A string: only information for function names containing this string
|
235 |
+
is printed.
|
236 |
+
|
237 |
+
* An integer: only these many lines are printed.
|
238 |
+
|
239 |
+
* A float (between 0 and 1): this fraction of the report is printed
|
240 |
+
(for example, use a limit of 0.4 to see the topmost 40% only).
|
241 |
+
|
242 |
+
You can combine several limits with repeated use of the option. For
|
243 |
+
example, ``-l __init__ -l 5`` will print only the topmost 5 lines of
|
244 |
+
information about class constructors.
|
245 |
+
|
246 |
+
-r
|
247 |
+
return the pstats.Stats object generated by the profiling. This
|
248 |
+
object has all the information about the profile in it, and you can
|
249 |
+
later use it for further analysis or in other functions.
|
250 |
+
|
251 |
+
-s <key>
|
252 |
+
sort profile by given key. You can provide more than one key
|
253 |
+
by using the option several times: '-s key1 -s key2 -s key3...'. The
|
254 |
+
default sorting key is 'time'.
|
255 |
+
|
256 |
+
The following is copied verbatim from the profile documentation
|
257 |
+
referenced below:
|
258 |
+
|
259 |
+
When more than one key is provided, additional keys are used as
|
260 |
+
secondary criteria when the there is equality in all keys selected
|
261 |
+
before them.
|
262 |
+
|
263 |
+
Abbreviations can be used for any key names, as long as the
|
264 |
+
abbreviation is unambiguous. The following are the keys currently
|
265 |
+
defined:
|
266 |
+
|
267 |
+
============ =====================
|
268 |
+
Valid Arg Meaning
|
269 |
+
============ =====================
|
270 |
+
"calls" call count
|
271 |
+
"cumulative" cumulative time
|
272 |
+
"file" file name
|
273 |
+
"module" file name
|
274 |
+
"pcalls" primitive call count
|
275 |
+
"line" line number
|
276 |
+
"name" function name
|
277 |
+
"nfl" name/file/line
|
278 |
+
"stdname" standard name
|
279 |
+
"time" internal time
|
280 |
+
============ =====================
|
281 |
+
|
282 |
+
Note that all sorts on statistics are in descending order (placing
|
283 |
+
most time consuming items first), where as name, file, and line number
|
284 |
+
searches are in ascending order (i.e., alphabetical). The subtle
|
285 |
+
distinction between "nfl" and "stdname" is that the standard name is a
|
286 |
+
sort of the name as printed, which means that the embedded line
|
287 |
+
numbers get compared in an odd way. For example, lines 3, 20, and 40
|
288 |
+
would (if the file names were the same) appear in the string order
|
289 |
+
"20" "3" and "40". In contrast, "nfl" does a numeric compare of the
|
290 |
+
line numbers. In fact, sort_stats("nfl") is the same as
|
291 |
+
sort_stats("name", "file", "line").
|
292 |
+
|
293 |
+
-T <filename>
|
294 |
+
save profile results as shown on screen to a text
|
295 |
+
file. The profile is still shown on screen.
|
296 |
+
|
297 |
+
-D <filename>
|
298 |
+
save (via dump_stats) profile statistics to given
|
299 |
+
filename. This data is in a format understood by the pstats module, and
|
300 |
+
is generated by a call to the dump_stats() method of profile
|
301 |
+
objects. The profile is still shown on screen.
|
302 |
+
|
303 |
+
-q
|
304 |
+
suppress output to the pager. Best used with -T and/or -D above.
|
305 |
+
|
306 |
+
If you want to run complete programs under the profiler's control, use
|
307 |
+
``%run -p [prof_opts] filename.py [args to program]`` where prof_opts
|
308 |
+
contains profiler specific options as described here.
|
309 |
+
|
310 |
+
You can read the complete documentation for the profile module with::
|
311 |
+
|
312 |
+
In [1]: import profile; profile.help()
|
313 |
+
|
314 |
+
.. versionchanged:: 7.3
|
315 |
+
User variables are no longer expanded,
|
316 |
+
the magic line is always left unmodified.
|
317 |
+
|
318 |
+
"""
|
319 |
+
opts, arg_str = self.parse_options(parameter_s, 'D:l:rs:T:q',
|
320 |
+
list_all=True, posix=False)
|
321 |
+
if cell is not None:
|
322 |
+
arg_str += '\n' + cell
|
323 |
+
arg_str = self.shell.transform_cell(arg_str)
|
324 |
+
return self._run_with_profiler(arg_str, opts, self.shell.user_ns)
|
325 |
+
|
326 |
+
def _run_with_profiler(self, code, opts, namespace):
|
327 |
+
"""
|
328 |
+
Run `code` with profiler. Used by ``%prun`` and ``%run -p``.
|
329 |
+
|
330 |
+
Parameters
|
331 |
+
----------
|
332 |
+
code : str
|
333 |
+
Code to be executed.
|
334 |
+
opts : Struct
|
335 |
+
Options parsed by `self.parse_options`.
|
336 |
+
namespace : dict
|
337 |
+
A dictionary for Python namespace (e.g., `self.shell.user_ns`).
|
338 |
+
|
339 |
+
"""
|
340 |
+
|
341 |
+
# Fill default values for unspecified options:
|
342 |
+
opts.merge(Struct(D=[''], l=[], s=['time'], T=['']))
|
343 |
+
|
344 |
+
prof = profile.Profile()
|
345 |
+
try:
|
346 |
+
prof = prof.runctx(code, namespace, namespace)
|
347 |
+
sys_exit = ''
|
348 |
+
except SystemExit:
|
349 |
+
sys_exit = """*** SystemExit exception caught in code being profiled."""
|
350 |
+
|
351 |
+
stats = pstats.Stats(prof).strip_dirs().sort_stats(*opts.s)
|
352 |
+
|
353 |
+
lims = opts.l
|
354 |
+
if lims:
|
355 |
+
lims = [] # rebuild lims with ints/floats/strings
|
356 |
+
for lim in opts.l:
|
357 |
+
try:
|
358 |
+
lims.append(int(lim))
|
359 |
+
except ValueError:
|
360 |
+
try:
|
361 |
+
lims.append(float(lim))
|
362 |
+
except ValueError:
|
363 |
+
lims.append(lim)
|
364 |
+
|
365 |
+
# Trap output.
|
366 |
+
stdout_trap = StringIO()
|
367 |
+
stats_stream = stats.stream
|
368 |
+
try:
|
369 |
+
stats.stream = stdout_trap
|
370 |
+
stats.print_stats(*lims)
|
371 |
+
finally:
|
372 |
+
stats.stream = stats_stream
|
373 |
+
|
374 |
+
output = stdout_trap.getvalue()
|
375 |
+
output = output.rstrip()
|
376 |
+
|
377 |
+
if 'q' not in opts:
|
378 |
+
page.page(output)
|
379 |
+
print(sys_exit, end=' ')
|
380 |
+
|
381 |
+
dump_file = opts.D[0]
|
382 |
+
text_file = opts.T[0]
|
383 |
+
if dump_file:
|
384 |
+
prof.dump_stats(dump_file)
|
385 |
+
print(
|
386 |
+
f"\n*** Profile stats marshalled to file {repr(dump_file)}.{sys_exit}"
|
387 |
+
)
|
388 |
+
if text_file:
|
389 |
+
pfile = Path(text_file)
|
390 |
+
pfile.touch(exist_ok=True)
|
391 |
+
pfile.write_text(output, encoding="utf-8")
|
392 |
+
|
393 |
+
print(
|
394 |
+
f"\n*** Profile printout saved to text file {repr(text_file)}.{sys_exit}"
|
395 |
+
)
|
396 |
+
|
397 |
+
if 'r' in opts:
|
398 |
+
return stats
|
399 |
+
|
400 |
+
return None
|
401 |
+
|
402 |
+
@line_magic
|
403 |
+
def pdb(self, parameter_s=''):
|
404 |
+
"""Control the automatic calling of the pdb interactive debugger.
|
405 |
+
|
406 |
+
Call as '%pdb on', '%pdb 1', '%pdb off' or '%pdb 0'. If called without
|
407 |
+
argument it works as a toggle.
|
408 |
+
|
409 |
+
When an exception is triggered, IPython can optionally call the
|
410 |
+
interactive pdb debugger after the traceback printout. %pdb toggles
|
411 |
+
this feature on and off.
|
412 |
+
|
413 |
+
The initial state of this feature is set in your configuration
|
414 |
+
file (the option is ``InteractiveShell.pdb``).
|
415 |
+
|
416 |
+
If you want to just activate the debugger AFTER an exception has fired,
|
417 |
+
without having to type '%pdb on' and rerunning your code, you can use
|
418 |
+
the %debug magic."""
|
419 |
+
|
420 |
+
par = parameter_s.strip().lower()
|
421 |
+
|
422 |
+
if par:
|
423 |
+
try:
|
424 |
+
new_pdb = {'off':0,'0':0,'on':1,'1':1}[par]
|
425 |
+
except KeyError:
|
426 |
+
print ('Incorrect argument. Use on/1, off/0, '
|
427 |
+
'or nothing for a toggle.')
|
428 |
+
return
|
429 |
+
else:
|
430 |
+
# toggle
|
431 |
+
new_pdb = not self.shell.call_pdb
|
432 |
+
|
433 |
+
# set on the shell
|
434 |
+
self.shell.call_pdb = new_pdb
|
435 |
+
print('Automatic pdb calling has been turned',on_off(new_pdb))
|
436 |
+
|
437 |
+
@magic_arguments.magic_arguments()
|
438 |
+
@magic_arguments.argument('--breakpoint', '-b', metavar='FILE:LINE',
|
439 |
+
help="""
|
440 |
+
Set break point at LINE in FILE.
|
441 |
+
"""
|
442 |
+
)
|
443 |
+
@magic_arguments.argument('statement', nargs='*',
|
444 |
+
help="""
|
445 |
+
Code to run in debugger.
|
446 |
+
You can omit this in cell magic mode.
|
447 |
+
"""
|
448 |
+
)
|
449 |
+
@no_var_expand
|
450 |
+
@line_cell_magic
|
451 |
+
@needs_local_scope
|
452 |
+
def debug(self, line="", cell=None, local_ns=None):
|
453 |
+
"""Activate the interactive debugger.
|
454 |
+
|
455 |
+
This magic command support two ways of activating debugger.
|
456 |
+
One is to activate debugger before executing code. This way, you
|
457 |
+
can set a break point, to step through the code from the point.
|
458 |
+
You can use this mode by giving statements to execute and optionally
|
459 |
+
a breakpoint.
|
460 |
+
|
461 |
+
The other one is to activate debugger in post-mortem mode. You can
|
462 |
+
activate this mode simply running %debug without any argument.
|
463 |
+
If an exception has just occurred, this lets you inspect its stack
|
464 |
+
frames interactively. Note that this will always work only on the last
|
465 |
+
traceback that occurred, so you must call this quickly after an
|
466 |
+
exception that you wish to inspect has fired, because if another one
|
467 |
+
occurs, it clobbers the previous one.
|
468 |
+
|
469 |
+
If you want IPython to automatically do this on every exception, see
|
470 |
+
the %pdb magic for more details.
|
471 |
+
|
472 |
+
.. versionchanged:: 7.3
|
473 |
+
When running code, user variables are no longer expanded,
|
474 |
+
the magic line is always left unmodified.
|
475 |
+
|
476 |
+
"""
|
477 |
+
args = magic_arguments.parse_argstring(self.debug, line)
|
478 |
+
|
479 |
+
if not (args.breakpoint or args.statement or cell):
|
480 |
+
self._debug_post_mortem()
|
481 |
+
elif not (args.breakpoint or cell):
|
482 |
+
# If there is no breakpoints, the line is just code to execute
|
483 |
+
self._debug_exec(line, None, local_ns)
|
484 |
+
else:
|
485 |
+
# Here we try to reconstruct the code from the output of
|
486 |
+
# parse_argstring. This might not work if the code has spaces
|
487 |
+
# For example this fails for `print("a b")`
|
488 |
+
code = "\n".join(args.statement)
|
489 |
+
if cell:
|
490 |
+
code += "\n" + cell
|
491 |
+
self._debug_exec(code, args.breakpoint, local_ns)
|
492 |
+
|
493 |
+
def _debug_post_mortem(self):
|
494 |
+
self.shell.debugger(force=True)
|
495 |
+
|
496 |
+
def _debug_exec(self, code, breakpoint, local_ns=None):
|
497 |
+
if breakpoint:
|
498 |
+
(filename, bp_line) = breakpoint.rsplit(':', 1)
|
499 |
+
bp_line = int(bp_line)
|
500 |
+
else:
|
501 |
+
(filename, bp_line) = (None, None)
|
502 |
+
self._run_with_debugger(
|
503 |
+
code, self.shell.user_ns, filename, bp_line, local_ns=local_ns
|
504 |
+
)
|
505 |
+
|
506 |
+
@line_magic
|
507 |
+
def tb(self, s):
|
508 |
+
"""Print the last traceback.
|
509 |
+
|
510 |
+
Optionally, specify an exception reporting mode, tuning the
|
511 |
+
verbosity of the traceback. By default the currently-active exception
|
512 |
+
mode is used. See %xmode for changing exception reporting modes.
|
513 |
+
|
514 |
+
Valid modes: Plain, Context, Verbose, and Minimal.
|
515 |
+
"""
|
516 |
+
interactive_tb = self.shell.InteractiveTB
|
517 |
+
if s:
|
518 |
+
# Switch exception reporting mode for this one call.
|
519 |
+
# Ensure it is switched back.
|
520 |
+
def xmode_switch_err(name):
|
521 |
+
warn('Error changing %s exception modes.\n%s' %
|
522 |
+
(name,sys.exc_info()[1]))
|
523 |
+
|
524 |
+
new_mode = s.strip().capitalize()
|
525 |
+
original_mode = interactive_tb.mode
|
526 |
+
try:
|
527 |
+
try:
|
528 |
+
interactive_tb.set_mode(mode=new_mode)
|
529 |
+
except Exception:
|
530 |
+
xmode_switch_err('user')
|
531 |
+
else:
|
532 |
+
self.shell.showtraceback()
|
533 |
+
finally:
|
534 |
+
interactive_tb.set_mode(mode=original_mode)
|
535 |
+
else:
|
536 |
+
self.shell.showtraceback()
|
537 |
+
|
538 |
+
@skip_doctest
|
539 |
+
@line_magic
|
540 |
+
def run(self, parameter_s='', runner=None,
|
541 |
+
file_finder=get_py_filename):
|
542 |
+
"""Run the named file inside IPython as a program.
|
543 |
+
|
544 |
+
Usage::
|
545 |
+
|
546 |
+
%run [-n -i -e -G]
|
547 |
+
[( -t [-N<N>] | -d [-b<N>] | -p [profile options] )]
|
548 |
+
( -m mod | filename ) [args]
|
549 |
+
|
550 |
+
The filename argument should be either a pure Python script (with
|
551 |
+
extension ``.py``), or a file with custom IPython syntax (such as
|
552 |
+
magics). If the latter, the file can be either a script with ``.ipy``
|
553 |
+
extension, or a Jupyter notebook with ``.ipynb`` extension. When running
|
554 |
+
a Jupyter notebook, the output from print statements and other
|
555 |
+
displayed objects will appear in the terminal (even matplotlib figures
|
556 |
+
will open, if a terminal-compliant backend is being used). Note that,
|
557 |
+
at the system command line, the ``jupyter run`` command offers similar
|
558 |
+
functionality for executing notebooks (albeit currently with some
|
559 |
+
differences in supported options).
|
560 |
+
|
561 |
+
Parameters after the filename are passed as command-line arguments to
|
562 |
+
the program (put in sys.argv). Then, control returns to IPython's
|
563 |
+
prompt.
|
564 |
+
|
565 |
+
This is similar to running at a system prompt ``python file args``,
|
566 |
+
but with the advantage of giving you IPython's tracebacks, and of
|
567 |
+
loading all variables into your interactive namespace for further use
|
568 |
+
(unless -p is used, see below).
|
569 |
+
|
570 |
+
The file is executed in a namespace initially consisting only of
|
571 |
+
``__name__=='__main__'`` and sys.argv constructed as indicated. It thus
|
572 |
+
sees its environment as if it were being run as a stand-alone program
|
573 |
+
(except for sharing global objects such as previously imported
|
574 |
+
modules). But after execution, the IPython interactive namespace gets
|
575 |
+
updated with all variables defined in the program (except for ``__name__``
|
576 |
+
and ``sys.argv``). This allows for very convenient loading of code for
|
577 |
+
interactive work, while giving each program a 'clean sheet' to run in.
|
578 |
+
|
579 |
+
Arguments are expanded using shell-like glob match. Patterns
|
580 |
+
'*', '?', '[seq]' and '[!seq]' can be used. Additionally,
|
581 |
+
tilde '~' will be expanded into user's home directory. Unlike
|
582 |
+
real shells, quotation does not suppress expansions. Use
|
583 |
+
*two* back slashes (e.g. ``\\\\*``) to suppress expansions.
|
584 |
+
To completely disable these expansions, you can use -G flag.
|
585 |
+
|
586 |
+
On Windows systems, the use of single quotes `'` when specifying
|
587 |
+
a file is not supported. Use double quotes `"`.
|
588 |
+
|
589 |
+
Options:
|
590 |
+
|
591 |
+
-n
|
592 |
+
__name__ is NOT set to '__main__', but to the running file's name
|
593 |
+
without extension (as python does under import). This allows running
|
594 |
+
scripts and reloading the definitions in them without calling code
|
595 |
+
protected by an ``if __name__ == "__main__"`` clause.
|
596 |
+
|
597 |
+
-i
|
598 |
+
run the file in IPython's namespace instead of an empty one. This
|
599 |
+
is useful if you are experimenting with code written in a text editor
|
600 |
+
which depends on variables defined interactively.
|
601 |
+
|
602 |
+
-e
|
603 |
+
ignore sys.exit() calls or SystemExit exceptions in the script
|
604 |
+
being run. This is particularly useful if IPython is being used to
|
605 |
+
run unittests, which always exit with a sys.exit() call. In such
|
606 |
+
cases you are interested in the output of the test results, not in
|
607 |
+
seeing a traceback of the unittest module.
|
608 |
+
|
609 |
+
-t
|
610 |
+
print timing information at the end of the run. IPython will give
|
611 |
+
you an estimated CPU time consumption for your script, which under
|
612 |
+
Unix uses the resource module to avoid the wraparound problems of
|
613 |
+
time.clock(). Under Unix, an estimate of time spent on system tasks
|
614 |
+
is also given (for Windows platforms this is reported as 0.0).
|
615 |
+
|
616 |
+
If -t is given, an additional ``-N<N>`` option can be given, where <N>
|
617 |
+
must be an integer indicating how many times you want the script to
|
618 |
+
run. The final timing report will include total and per run results.
|
619 |
+
|
620 |
+
For example (testing the script uniq_stable.py)::
|
621 |
+
|
622 |
+
In [1]: run -t uniq_stable
|
623 |
+
|
624 |
+
IPython CPU timings (estimated):
|
625 |
+
User : 0.19597 s.
|
626 |
+
System: 0.0 s.
|
627 |
+
|
628 |
+
In [2]: run -t -N5 uniq_stable
|
629 |
+
|
630 |
+
IPython CPU timings (estimated):
|
631 |
+
Total runs performed: 5
|
632 |
+
Times : Total Per run
|
633 |
+
User : 0.910862 s, 0.1821724 s.
|
634 |
+
System: 0.0 s, 0.0 s.
|
635 |
+
|
636 |
+
-d
|
637 |
+
run your program under the control of pdb, the Python debugger.
|
638 |
+
This allows you to execute your program step by step, watch variables,
|
639 |
+
etc. Internally, what IPython does is similar to calling::
|
640 |
+
|
641 |
+
pdb.run('execfile("YOURFILENAME")')
|
642 |
+
|
643 |
+
with a breakpoint set on line 1 of your file. You can change the line
|
644 |
+
number for this automatic breakpoint to be <N> by using the -bN option
|
645 |
+
(where N must be an integer). For example::
|
646 |
+
|
647 |
+
%run -d -b40 myscript
|
648 |
+
|
649 |
+
will set the first breakpoint at line 40 in myscript.py. Note that
|
650 |
+
the first breakpoint must be set on a line which actually does
|
651 |
+
something (not a comment or docstring) for it to stop execution.
|
652 |
+
|
653 |
+
Or you can specify a breakpoint in a different file::
|
654 |
+
|
655 |
+
%run -d -b myotherfile.py:20 myscript
|
656 |
+
|
657 |
+
When the pdb debugger starts, you will see a (Pdb) prompt. You must
|
658 |
+
first enter 'c' (without quotes) to start execution up to the first
|
659 |
+
breakpoint.
|
660 |
+
|
661 |
+
Entering 'help' gives information about the use of the debugger. You
|
662 |
+
can easily see pdb's full documentation with "import pdb;pdb.help()"
|
663 |
+
at a prompt.
|
664 |
+
|
665 |
+
-p
|
666 |
+
run program under the control of the Python profiler module (which
|
667 |
+
prints a detailed report of execution times, function calls, etc).
|
668 |
+
|
669 |
+
You can pass other options after -p which affect the behavior of the
|
670 |
+
profiler itself. See the docs for %prun for details.
|
671 |
+
|
672 |
+
In this mode, the program's variables do NOT propagate back to the
|
673 |
+
IPython interactive namespace (because they remain in the namespace
|
674 |
+
where the profiler executes them).
|
675 |
+
|
676 |
+
Internally this triggers a call to %prun, see its documentation for
|
677 |
+
details on the options available specifically for profiling.
|
678 |
+
|
679 |
+
There is one special usage for which the text above doesn't apply:
|
680 |
+
if the filename ends with .ipy[nb], the file is run as ipython script,
|
681 |
+
just as if the commands were written on IPython prompt.
|
682 |
+
|
683 |
+
-m
|
684 |
+
specify module name to load instead of script path. Similar to
|
685 |
+
the -m option for the python interpreter. Use this option last if you
|
686 |
+
want to combine with other %run options. Unlike the python interpreter
|
687 |
+
only source modules are allowed no .pyc or .pyo files.
|
688 |
+
For example::
|
689 |
+
|
690 |
+
%run -m example
|
691 |
+
|
692 |
+
will run the example module.
|
693 |
+
|
694 |
+
-G
|
695 |
+
disable shell-like glob expansion of arguments.
|
696 |
+
|
697 |
+
"""
|
698 |
+
|
699 |
+
# Logic to handle issue #3664
|
700 |
+
# Add '--' after '-m <module_name>' to ignore additional args passed to a module.
|
701 |
+
if '-m' in parameter_s and '--' not in parameter_s:
|
702 |
+
argv = shlex.split(parameter_s, posix=(os.name == 'posix'))
|
703 |
+
for idx, arg in enumerate(argv):
|
704 |
+
if arg and arg.startswith('-') and arg != '-':
|
705 |
+
if arg == '-m':
|
706 |
+
argv.insert(idx + 2, '--')
|
707 |
+
break
|
708 |
+
else:
|
709 |
+
# Positional arg, break
|
710 |
+
break
|
711 |
+
parameter_s = ' '.join(shlex.quote(arg) for arg in argv)
|
712 |
+
|
713 |
+
# get arguments and set sys.argv for program to be run.
|
714 |
+
opts, arg_lst = self.parse_options(parameter_s,
|
715 |
+
'nidtN:b:pD:l:rs:T:em:G',
|
716 |
+
mode='list', list_all=1)
|
717 |
+
if "m" in opts:
|
718 |
+
modulename = opts["m"][0]
|
719 |
+
modpath = find_mod(modulename)
|
720 |
+
if modpath is None:
|
721 |
+
msg = '%r is not a valid modulename on sys.path'%modulename
|
722 |
+
raise Exception(msg)
|
723 |
+
arg_lst = [modpath] + arg_lst
|
724 |
+
try:
|
725 |
+
fpath = None # initialize to make sure fpath is in scope later
|
726 |
+
fpath = arg_lst[0]
|
727 |
+
filename = file_finder(fpath)
|
728 |
+
except IndexError as e:
|
729 |
+
msg = 'you must provide at least a filename.'
|
730 |
+
raise Exception(msg) from e
|
731 |
+
except IOError as e:
|
732 |
+
try:
|
733 |
+
msg = str(e)
|
734 |
+
except UnicodeError:
|
735 |
+
msg = e.message
|
736 |
+
if os.name == 'nt' and re.match(r"^'.*'$",fpath):
|
737 |
+
warn('For Windows, use double quotes to wrap a filename: %run "mypath\\myfile.py"')
|
738 |
+
raise Exception(msg) from e
|
739 |
+
except TypeError:
|
740 |
+
if fpath in sys.meta_path:
|
741 |
+
filename = ""
|
742 |
+
else:
|
743 |
+
raise
|
744 |
+
|
745 |
+
if filename.lower().endswith(('.ipy', '.ipynb')):
|
746 |
+
with preserve_keys(self.shell.user_ns, '__file__'):
|
747 |
+
self.shell.user_ns['__file__'] = filename
|
748 |
+
self.shell.safe_execfile_ipy(filename, raise_exceptions=True)
|
749 |
+
return
|
750 |
+
|
751 |
+
# Control the response to exit() calls made by the script being run
|
752 |
+
exit_ignore = 'e' in opts
|
753 |
+
|
754 |
+
# Make sure that the running script gets a proper sys.argv as if it
|
755 |
+
# were run from a system shell.
|
756 |
+
save_argv = sys.argv # save it for later restoring
|
757 |
+
|
758 |
+
if 'G' in opts:
|
759 |
+
args = arg_lst[1:]
|
760 |
+
else:
|
761 |
+
# tilde and glob expansion
|
762 |
+
args = shellglob(map(os.path.expanduser, arg_lst[1:]))
|
763 |
+
|
764 |
+
sys.argv = [filename] + args # put in the proper filename
|
765 |
+
|
766 |
+
if 'n' in opts:
|
767 |
+
name = Path(filename).stem
|
768 |
+
else:
|
769 |
+
name = '__main__'
|
770 |
+
|
771 |
+
if 'i' in opts:
|
772 |
+
# Run in user's interactive namespace
|
773 |
+
prog_ns = self.shell.user_ns
|
774 |
+
__name__save = self.shell.user_ns['__name__']
|
775 |
+
prog_ns['__name__'] = name
|
776 |
+
main_mod = self.shell.user_module
|
777 |
+
|
778 |
+
# Since '%run foo' emulates 'python foo.py' at the cmd line, we must
|
779 |
+
# set the __file__ global in the script's namespace
|
780 |
+
# TK: Is this necessary in interactive mode?
|
781 |
+
prog_ns['__file__'] = filename
|
782 |
+
else:
|
783 |
+
# Run in a fresh, empty namespace
|
784 |
+
|
785 |
+
# The shell MUST hold a reference to prog_ns so after %run
|
786 |
+
# exits, the python deletion mechanism doesn't zero it out
|
787 |
+
# (leaving dangling references). See interactiveshell for details
|
788 |
+
main_mod = self.shell.new_main_mod(filename, name)
|
789 |
+
prog_ns = main_mod.__dict__
|
790 |
+
|
791 |
+
# pickle fix. See interactiveshell for an explanation. But we need to
|
792 |
+
# make sure that, if we overwrite __main__, we replace it at the end
|
793 |
+
main_mod_name = prog_ns['__name__']
|
794 |
+
|
795 |
+
if main_mod_name == '__main__':
|
796 |
+
restore_main = sys.modules['__main__']
|
797 |
+
else:
|
798 |
+
restore_main = False
|
799 |
+
|
800 |
+
# This needs to be undone at the end to prevent holding references to
|
801 |
+
# every single object ever created.
|
802 |
+
sys.modules[main_mod_name] = main_mod
|
803 |
+
|
804 |
+
if 'p' in opts or 'd' in opts:
|
805 |
+
if 'm' in opts:
|
806 |
+
code = 'run_module(modulename, prog_ns)'
|
807 |
+
code_ns = {
|
808 |
+
'run_module': self.shell.safe_run_module,
|
809 |
+
'prog_ns': prog_ns,
|
810 |
+
'modulename': modulename,
|
811 |
+
}
|
812 |
+
else:
|
813 |
+
if 'd' in opts:
|
814 |
+
# allow exceptions to raise in debug mode
|
815 |
+
code = 'execfile(filename, prog_ns, raise_exceptions=True)'
|
816 |
+
else:
|
817 |
+
code = 'execfile(filename, prog_ns)'
|
818 |
+
code_ns = {
|
819 |
+
'execfile': self.shell.safe_execfile,
|
820 |
+
'prog_ns': prog_ns,
|
821 |
+
'filename': get_py_filename(filename),
|
822 |
+
}
|
823 |
+
|
824 |
+
try:
|
825 |
+
stats = None
|
826 |
+
if 'p' in opts:
|
827 |
+
stats = self._run_with_profiler(code, opts, code_ns)
|
828 |
+
else:
|
829 |
+
if 'd' in opts:
|
830 |
+
bp_file, bp_line = parse_breakpoint(
|
831 |
+
opts.get('b', ['1'])[0], filename)
|
832 |
+
self._run_with_debugger(
|
833 |
+
code, code_ns, filename, bp_line, bp_file)
|
834 |
+
else:
|
835 |
+
if 'm' in opts:
|
836 |
+
def run():
|
837 |
+
self.shell.safe_run_module(modulename, prog_ns)
|
838 |
+
else:
|
839 |
+
if runner is None:
|
840 |
+
runner = self.default_runner
|
841 |
+
if runner is None:
|
842 |
+
runner = self.shell.safe_execfile
|
843 |
+
|
844 |
+
def run():
|
845 |
+
runner(filename, prog_ns, prog_ns,
|
846 |
+
exit_ignore=exit_ignore)
|
847 |
+
|
848 |
+
if 't' in opts:
|
849 |
+
# timed execution
|
850 |
+
try:
|
851 |
+
nruns = int(opts['N'][0])
|
852 |
+
if nruns < 1:
|
853 |
+
error('Number of runs must be >=1')
|
854 |
+
return
|
855 |
+
except (KeyError):
|
856 |
+
nruns = 1
|
857 |
+
self._run_with_timing(run, nruns)
|
858 |
+
else:
|
859 |
+
# regular execution
|
860 |
+
run()
|
861 |
+
|
862 |
+
if 'i' in opts:
|
863 |
+
self.shell.user_ns['__name__'] = __name__save
|
864 |
+
else:
|
865 |
+
# update IPython interactive namespace
|
866 |
+
|
867 |
+
# Some forms of read errors on the file may mean the
|
868 |
+
# __name__ key was never set; using pop we don't have to
|
869 |
+
# worry about a possible KeyError.
|
870 |
+
prog_ns.pop('__name__', None)
|
871 |
+
|
872 |
+
with preserve_keys(self.shell.user_ns, '__file__'):
|
873 |
+
self.shell.user_ns.update(prog_ns)
|
874 |
+
finally:
|
875 |
+
# It's a bit of a mystery why, but __builtins__ can change from
|
876 |
+
# being a module to becoming a dict missing some key data after
|
877 |
+
# %run. As best I can see, this is NOT something IPython is doing
|
878 |
+
# at all, and similar problems have been reported before:
|
879 |
+
# http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html
|
880 |
+
# Since this seems to be done by the interpreter itself, the best
|
881 |
+
# we can do is to at least restore __builtins__ for the user on
|
882 |
+
# exit.
|
883 |
+
self.shell.user_ns['__builtins__'] = builtin_mod
|
884 |
+
|
885 |
+
# Ensure key global structures are restored
|
886 |
+
sys.argv = save_argv
|
887 |
+
if restore_main:
|
888 |
+
sys.modules['__main__'] = restore_main
|
889 |
+
if '__mp_main__' in sys.modules:
|
890 |
+
sys.modules['__mp_main__'] = restore_main
|
891 |
+
else:
|
892 |
+
# Remove from sys.modules the reference to main_mod we'd
|
893 |
+
# added. Otherwise it will trap references to objects
|
894 |
+
# contained therein.
|
895 |
+
del sys.modules[main_mod_name]
|
896 |
+
|
897 |
+
return stats
|
898 |
+
|
899 |
+
def _run_with_debugger(
|
900 |
+
self, code, code_ns, filename=None, bp_line=None, bp_file=None, local_ns=None
|
901 |
+
):
|
902 |
+
"""
|
903 |
+
Run `code` in debugger with a break point.
|
904 |
+
|
905 |
+
Parameters
|
906 |
+
----------
|
907 |
+
code : str
|
908 |
+
Code to execute.
|
909 |
+
code_ns : dict
|
910 |
+
A namespace in which `code` is executed.
|
911 |
+
filename : str
|
912 |
+
`code` is ran as if it is in `filename`.
|
913 |
+
bp_line : int, optional
|
914 |
+
Line number of the break point.
|
915 |
+
bp_file : str, optional
|
916 |
+
Path to the file in which break point is specified.
|
917 |
+
`filename` is used if not given.
|
918 |
+
local_ns : dict, optional
|
919 |
+
A local namespace in which `code` is executed.
|
920 |
+
|
921 |
+
Raises
|
922 |
+
------
|
923 |
+
UsageError
|
924 |
+
If the break point given by `bp_line` is not valid.
|
925 |
+
|
926 |
+
"""
|
927 |
+
deb = self.shell.InteractiveTB.pdb
|
928 |
+
if not deb:
|
929 |
+
self.shell.InteractiveTB.pdb = self.shell.InteractiveTB.debugger_cls()
|
930 |
+
deb = self.shell.InteractiveTB.pdb
|
931 |
+
|
932 |
+
# deb.checkline() fails if deb.curframe exists but is None; it can
|
933 |
+
# handle it not existing. https://github.com/ipython/ipython/issues/10028
|
934 |
+
if hasattr(deb, 'curframe'):
|
935 |
+
del deb.curframe
|
936 |
+
|
937 |
+
# reset Breakpoint state, which is moronically kept
|
938 |
+
# in a class
|
939 |
+
bdb.Breakpoint.next = 1
|
940 |
+
bdb.Breakpoint.bplist = {}
|
941 |
+
bdb.Breakpoint.bpbynumber = [None]
|
942 |
+
deb.clear_all_breaks()
|
943 |
+
if bp_line is not None:
|
944 |
+
# Set an initial breakpoint to stop execution
|
945 |
+
maxtries = 10
|
946 |
+
bp_file = bp_file or filename
|
947 |
+
checkline = deb.checkline(bp_file, bp_line)
|
948 |
+
if not checkline:
|
949 |
+
for bp in range(bp_line + 1, bp_line + maxtries + 1):
|
950 |
+
if deb.checkline(bp_file, bp):
|
951 |
+
break
|
952 |
+
else:
|
953 |
+
msg = ("\nI failed to find a valid line to set "
|
954 |
+
"a breakpoint\n"
|
955 |
+
"after trying up to line: %s.\n"
|
956 |
+
"Please set a valid breakpoint manually "
|
957 |
+
"with the -b option." % bp)
|
958 |
+
raise UsageError(msg)
|
959 |
+
# if we find a good linenumber, set the breakpoint
|
960 |
+
deb.do_break('%s:%s' % (bp_file, bp_line))
|
961 |
+
|
962 |
+
if filename:
|
963 |
+
# Mimic Pdb._runscript(...)
|
964 |
+
deb._wait_for_mainpyfile = True
|
965 |
+
deb.mainpyfile = deb.canonic(filename)
|
966 |
+
|
967 |
+
# Start file run
|
968 |
+
print("NOTE: Enter 'c' at the %s prompt to continue execution." % deb.prompt)
|
969 |
+
try:
|
970 |
+
if filename:
|
971 |
+
# save filename so it can be used by methods on the deb object
|
972 |
+
deb._exec_filename = filename
|
973 |
+
while True:
|
974 |
+
try:
|
975 |
+
trace = sys.gettrace()
|
976 |
+
deb.run(code, code_ns, local_ns)
|
977 |
+
except Restart:
|
978 |
+
print("Restarting")
|
979 |
+
if filename:
|
980 |
+
deb._wait_for_mainpyfile = True
|
981 |
+
deb.mainpyfile = deb.canonic(filename)
|
982 |
+
continue
|
983 |
+
else:
|
984 |
+
break
|
985 |
+
finally:
|
986 |
+
sys.settrace(trace)
|
987 |
+
|
988 |
+
# Perform proper cleanup of the session in case if
|
989 |
+
# it exited with "continue" and not "quit" command
|
990 |
+
if hasattr(deb, "rcLines"):
|
991 |
+
# Run this code defensively in case if custom debugger
|
992 |
+
# class does not implement rcLines, which although public
|
993 |
+
# is an implementation detail of `pdb.Pdb` and not part of
|
994 |
+
# the more generic basic debugger framework (`bdb.Bdb`).
|
995 |
+
deb.set_quit()
|
996 |
+
deb.rcLines.extend(["q"])
|
997 |
+
try:
|
998 |
+
deb.run("", code_ns, local_ns)
|
999 |
+
except StopIteration:
|
1000 |
+
# Stop iteration is raised on quit command
|
1001 |
+
pass
|
1002 |
+
|
1003 |
+
except Exception:
|
1004 |
+
etype, value, tb = sys.exc_info()
|
1005 |
+
# Skip three frames in the traceback: the %run one,
|
1006 |
+
# one inside bdb.py, and the command-line typed by the
|
1007 |
+
# user (run by exec in pdb itself).
|
1008 |
+
self.shell.InteractiveTB(etype, value, tb, tb_offset=3)
|
1009 |
+
|
1010 |
+
@staticmethod
|
1011 |
+
def _run_with_timing(run, nruns):
|
1012 |
+
"""
|
1013 |
+
Run function `run` and print timing information.
|
1014 |
+
|
1015 |
+
Parameters
|
1016 |
+
----------
|
1017 |
+
run : callable
|
1018 |
+
Any callable object which takes no argument.
|
1019 |
+
nruns : int
|
1020 |
+
Number of times to execute `run`.
|
1021 |
+
|
1022 |
+
"""
|
1023 |
+
twall0 = time.perf_counter()
|
1024 |
+
if nruns == 1:
|
1025 |
+
t0 = clock2()
|
1026 |
+
run()
|
1027 |
+
t1 = clock2()
|
1028 |
+
t_usr = t1[0] - t0[0]
|
1029 |
+
t_sys = t1[1] - t0[1]
|
1030 |
+
print("\nIPython CPU timings (estimated):")
|
1031 |
+
print(" User : %10.2f s." % t_usr)
|
1032 |
+
print(" System : %10.2f s." % t_sys)
|
1033 |
+
else:
|
1034 |
+
runs = range(nruns)
|
1035 |
+
t0 = clock2()
|
1036 |
+
for nr in runs:
|
1037 |
+
run()
|
1038 |
+
t1 = clock2()
|
1039 |
+
t_usr = t1[0] - t0[0]
|
1040 |
+
t_sys = t1[1] - t0[1]
|
1041 |
+
print("\nIPython CPU timings (estimated):")
|
1042 |
+
print("Total runs performed:", nruns)
|
1043 |
+
print(" Times : %10s %10s" % ('Total', 'Per run'))
|
1044 |
+
print(" User : %10.2f s, %10.2f s." % (t_usr, t_usr / nruns))
|
1045 |
+
print(" System : %10.2f s, %10.2f s." % (t_sys, t_sys / nruns))
|
1046 |
+
twall1 = time.perf_counter()
|
1047 |
+
print("Wall time: %10.2f s." % (twall1 - twall0))
|
1048 |
+
|
1049 |
+
@skip_doctest
|
1050 |
+
@no_var_expand
|
1051 |
+
@line_cell_magic
|
1052 |
+
@needs_local_scope
|
1053 |
+
def timeit(self, line='', cell=None, local_ns=None):
|
1054 |
+
"""Time execution of a Python statement or expression
|
1055 |
+
|
1056 |
+
**Usage, in line mode**::
|
1057 |
+
|
1058 |
+
%timeit [-n<N> -r<R> [-t|-c] -q -p<P> [-o|-v <V>]] statement
|
1059 |
+
|
1060 |
+
**or in cell mode**::
|
1061 |
+
|
1062 |
+
%%timeit [-n<N> -r<R> [-t|-c] -q -p<P> [-o|-v <V>]] setup_code
|
1063 |
+
code
|
1064 |
+
code...
|
1065 |
+
|
1066 |
+
Time execution of a Python statement or expression using the timeit
|
1067 |
+
module. This function can be used both as a line and cell magic:
|
1068 |
+
|
1069 |
+
- In line mode you can time a single-line statement (though multiple
|
1070 |
+
ones can be chained with using semicolons).
|
1071 |
+
|
1072 |
+
- In cell mode, the statement in the first line is used as setup code
|
1073 |
+
(executed but not timed) and the body of the cell is timed. The cell
|
1074 |
+
body has access to any variables created in the setup code.
|
1075 |
+
|
1076 |
+
Options:
|
1077 |
+
|
1078 |
+
-n<N>
|
1079 |
+
Execute the given statement N times in a loop. If N is not
|
1080 |
+
provided, N is determined so as to get sufficient accuracy.
|
1081 |
+
|
1082 |
+
-r<R>
|
1083 |
+
Number of repeats R, each consisting of N loops, and take the
|
1084 |
+
average result.
|
1085 |
+
Default: 7
|
1086 |
+
|
1087 |
+
-t
|
1088 |
+
Use ``time.time`` to measure the time, which is the default on Unix.
|
1089 |
+
This function measures wall time.
|
1090 |
+
|
1091 |
+
-c
|
1092 |
+
Use ``time.clock`` to measure the time, which is the default on
|
1093 |
+
Windows and measures wall time. On Unix, ``resource.getrusage`` is used
|
1094 |
+
instead and returns the CPU user time.
|
1095 |
+
|
1096 |
+
-p<P>
|
1097 |
+
Use a precision of P digits to display the timing result.
|
1098 |
+
Default: 3
|
1099 |
+
|
1100 |
+
-q
|
1101 |
+
Quiet, do not print result.
|
1102 |
+
|
1103 |
+
-o
|
1104 |
+
Return a ``TimeitResult`` that can be stored in a variable to inspect
|
1105 |
+
the result in more details.
|
1106 |
+
|
1107 |
+
-v <V>
|
1108 |
+
Like ``-o``, but save the ``TimeitResult`` directly to variable <V>.
|
1109 |
+
|
1110 |
+
.. versionchanged:: 7.3
|
1111 |
+
User variables are no longer expanded,
|
1112 |
+
the magic line is always left unmodified.
|
1113 |
+
|
1114 |
+
Examples
|
1115 |
+
--------
|
1116 |
+
::
|
1117 |
+
|
1118 |
+
In [1]: %timeit pass
|
1119 |
+
8.26 ns ± 0.12 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)
|
1120 |
+
|
1121 |
+
In [2]: u = None
|
1122 |
+
|
1123 |
+
In [3]: %timeit u is None
|
1124 |
+
29.9 ns ± 0.643 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
|
1125 |
+
|
1126 |
+
In [4]: %timeit -r 4 u == None
|
1127 |
+
|
1128 |
+
In [5]: import time
|
1129 |
+
|
1130 |
+
In [6]: %timeit -n1 time.sleep(2)
|
1131 |
+
|
1132 |
+
The times reported by ``%timeit`` will be slightly higher than those
|
1133 |
+
reported by the timeit.py script when variables are accessed. This is
|
1134 |
+
due to the fact that ``%timeit`` executes the statement in the namespace
|
1135 |
+
of the shell, compared with timeit.py, which uses a single setup
|
1136 |
+
statement to import function or create variables. Generally, the bias
|
1137 |
+
does not matter as long as results from timeit.py are not mixed with
|
1138 |
+
those from ``%timeit``."""
|
1139 |
+
|
1140 |
+
opts, stmt = self.parse_options(
|
1141 |
+
line, "n:r:tcp:qov:", posix=False, strict=False, preserve_non_opts=True
|
1142 |
+
)
|
1143 |
+
if stmt == "" and cell is None:
|
1144 |
+
return
|
1145 |
+
|
1146 |
+
timefunc = timeit.default_timer
|
1147 |
+
number = int(getattr(opts, "n", 0))
|
1148 |
+
default_repeat = 7 if timeit.default_repeat < 7 else timeit.default_repeat
|
1149 |
+
repeat = int(getattr(opts, "r", default_repeat))
|
1150 |
+
precision = int(getattr(opts, "p", 3))
|
1151 |
+
quiet = "q" in opts
|
1152 |
+
return_result = "o" in opts
|
1153 |
+
save_result = "v" in opts
|
1154 |
+
if hasattr(opts, "t"):
|
1155 |
+
timefunc = time.time
|
1156 |
+
if hasattr(opts, "c"):
|
1157 |
+
timefunc = clock
|
1158 |
+
|
1159 |
+
timer = Timer(timer=timefunc)
|
1160 |
+
# this code has tight coupling to the inner workings of timeit.Timer,
|
1161 |
+
# but is there a better way to achieve that the code stmt has access
|
1162 |
+
# to the shell namespace?
|
1163 |
+
transform = self.shell.transform_cell
|
1164 |
+
|
1165 |
+
if cell is None:
|
1166 |
+
# called as line magic
|
1167 |
+
ast_setup = self.shell.compile.ast_parse("pass")
|
1168 |
+
ast_stmt = self.shell.compile.ast_parse(transform(stmt))
|
1169 |
+
else:
|
1170 |
+
ast_setup = self.shell.compile.ast_parse(transform(stmt))
|
1171 |
+
ast_stmt = self.shell.compile.ast_parse(transform(cell))
|
1172 |
+
|
1173 |
+
ast_setup = self.shell.transform_ast(ast_setup)
|
1174 |
+
ast_stmt = self.shell.transform_ast(ast_stmt)
|
1175 |
+
|
1176 |
+
# Check that these compile to valid Python code *outside* the timer func
|
1177 |
+
# Invalid code may become valid when put inside the function & loop,
|
1178 |
+
# which messes up error messages.
|
1179 |
+
# https://github.com/ipython/ipython/issues/10636
|
1180 |
+
self.shell.compile(ast_setup, "<magic-timeit-setup>", "exec")
|
1181 |
+
self.shell.compile(ast_stmt, "<magic-timeit-stmt>", "exec")
|
1182 |
+
|
1183 |
+
# This codestring is taken from timeit.template - we fill it in as an
|
1184 |
+
# AST, so that we can apply our AST transformations to the user code
|
1185 |
+
# without affecting the timing code.
|
1186 |
+
timeit_ast_template = ast.parse('def inner(_it, _timer):\n'
|
1187 |
+
' setup\n'
|
1188 |
+
' _t0 = _timer()\n'
|
1189 |
+
' for _i in _it:\n'
|
1190 |
+
' stmt\n'
|
1191 |
+
' _t1 = _timer()\n'
|
1192 |
+
' return _t1 - _t0\n')
|
1193 |
+
|
1194 |
+
timeit_ast = TimeitTemplateFiller(ast_setup, ast_stmt).visit(timeit_ast_template)
|
1195 |
+
timeit_ast = ast.fix_missing_locations(timeit_ast)
|
1196 |
+
|
1197 |
+
# Track compilation time so it can be reported if too long
|
1198 |
+
# Minimum time above which compilation time will be reported
|
1199 |
+
tc_min = 0.1
|
1200 |
+
|
1201 |
+
t0 = clock()
|
1202 |
+
code = self.shell.compile(timeit_ast, "<magic-timeit>", "exec")
|
1203 |
+
tc = clock()-t0
|
1204 |
+
|
1205 |
+
ns = {}
|
1206 |
+
glob = self.shell.user_ns
|
1207 |
+
# handles global vars with same name as local vars. We store them in conflict_globs.
|
1208 |
+
conflict_globs = {}
|
1209 |
+
if local_ns and cell is None:
|
1210 |
+
for var_name, var_val in glob.items():
|
1211 |
+
if var_name in local_ns:
|
1212 |
+
conflict_globs[var_name] = var_val
|
1213 |
+
glob.update(local_ns)
|
1214 |
+
|
1215 |
+
exec(code, glob, ns)
|
1216 |
+
timer.inner = ns["inner"]
|
1217 |
+
|
1218 |
+
# This is used to check if there is a huge difference between the
|
1219 |
+
# best and worst timings.
|
1220 |
+
# Issue: https://github.com/ipython/ipython/issues/6471
|
1221 |
+
if number == 0:
|
1222 |
+
# determine number so that 0.2 <= total time < 2.0
|
1223 |
+
for index in range(0, 10):
|
1224 |
+
number = 10 ** index
|
1225 |
+
time_number = timer.timeit(number)
|
1226 |
+
if time_number >= 0.2:
|
1227 |
+
break
|
1228 |
+
|
1229 |
+
all_runs = timer.repeat(repeat, number)
|
1230 |
+
best = min(all_runs) / number
|
1231 |
+
worst = max(all_runs) / number
|
1232 |
+
timeit_result = TimeitResult(number, repeat, best, worst, all_runs, tc, precision)
|
1233 |
+
|
1234 |
+
# Restore global vars from conflict_globs
|
1235 |
+
if conflict_globs:
|
1236 |
+
glob.update(conflict_globs)
|
1237 |
+
|
1238 |
+
if not quiet:
|
1239 |
+
# Check best timing is greater than zero to avoid a
|
1240 |
+
# ZeroDivisionError.
|
1241 |
+
# In cases where the slowest timing is lesser than a microsecond
|
1242 |
+
# we assume that it does not really matter if the fastest
|
1243 |
+
# timing is 4 times faster than the slowest timing or not.
|
1244 |
+
if worst > 4 * best and best > 0 and worst > 1e-6:
|
1245 |
+
print("The slowest run took %0.2f times longer than the "
|
1246 |
+
"fastest. This could mean that an intermediate result "
|
1247 |
+
"is being cached." % (worst / best))
|
1248 |
+
|
1249 |
+
print( timeit_result )
|
1250 |
+
|
1251 |
+
if tc > tc_min:
|
1252 |
+
print("Compiler time: %.2f s" % tc)
|
1253 |
+
|
1254 |
+
if save_result:
|
1255 |
+
self.shell.user_ns[opts.v] = timeit_result
|
1256 |
+
|
1257 |
+
if return_result:
|
1258 |
+
return timeit_result
|
1259 |
+
|
1260 |
+
@skip_doctest
|
1261 |
+
@no_var_expand
|
1262 |
+
@needs_local_scope
|
1263 |
+
@line_cell_magic
|
1264 |
+
@output_can_be_silenced
|
1265 |
+
def time(self, line="", cell=None, local_ns=None):
|
1266 |
+
"""Time execution of a Python statement or expression.
|
1267 |
+
|
1268 |
+
The CPU and wall clock times are printed, and the value of the
|
1269 |
+
expression (if any) is returned. Note that under Win32, system time
|
1270 |
+
is always reported as 0, since it can not be measured.
|
1271 |
+
|
1272 |
+
This function can be used both as a line and cell magic:
|
1273 |
+
|
1274 |
+
- In line mode you can time a single-line statement (though multiple
|
1275 |
+
ones can be chained with using semicolons).
|
1276 |
+
|
1277 |
+
- In cell mode, you can time the cell body (a directly
|
1278 |
+
following statement raises an error).
|
1279 |
+
|
1280 |
+
This function provides very basic timing functionality. Use the timeit
|
1281 |
+
magic for more control over the measurement.
|
1282 |
+
|
1283 |
+
.. versionchanged:: 7.3
|
1284 |
+
User variables are no longer expanded,
|
1285 |
+
the magic line is always left unmodified.
|
1286 |
+
|
1287 |
+
.. versionchanged:: 8.3
|
1288 |
+
The time magic now correctly propagates system-exiting exceptions
|
1289 |
+
(such as ``KeyboardInterrupt`` invoked when interrupting execution)
|
1290 |
+
rather than just printing out the exception traceback.
|
1291 |
+
The non-system-exception will still be caught as before.
|
1292 |
+
|
1293 |
+
Examples
|
1294 |
+
--------
|
1295 |
+
::
|
1296 |
+
|
1297 |
+
In [1]: %time 2**128
|
1298 |
+
CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
|
1299 |
+
Wall time: 0.00
|
1300 |
+
Out[1]: 340282366920938463463374607431768211456L
|
1301 |
+
|
1302 |
+
In [2]: n = 1000000
|
1303 |
+
|
1304 |
+
In [3]: %time sum(range(n))
|
1305 |
+
CPU times: user 1.20 s, sys: 0.05 s, total: 1.25 s
|
1306 |
+
Wall time: 1.37
|
1307 |
+
Out[3]: 499999500000L
|
1308 |
+
|
1309 |
+
In [4]: %time print('hello world')
|
1310 |
+
hello world
|
1311 |
+
CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
|
1312 |
+
Wall time: 0.00
|
1313 |
+
|
1314 |
+
.. note::
|
1315 |
+
The time needed by Python to compile the given expression will be
|
1316 |
+
reported if it is more than 0.1s.
|
1317 |
+
|
1318 |
+
In the example below, the actual exponentiation is done by Python
|
1319 |
+
at compilation time, so while the expression can take a noticeable
|
1320 |
+
amount of time to compute, that time is purely due to the
|
1321 |
+
compilation::
|
1322 |
+
|
1323 |
+
In [5]: %time 3**9999;
|
1324 |
+
CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
|
1325 |
+
Wall time: 0.00 s
|
1326 |
+
|
1327 |
+
In [6]: %time 3**999999;
|
1328 |
+
CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
|
1329 |
+
Wall time: 0.00 s
|
1330 |
+
Compiler : 0.78 s
|
1331 |
+
"""
|
1332 |
+
# fail immediately if the given expression can't be compiled
|
1333 |
+
|
1334 |
+
if line and cell:
|
1335 |
+
raise UsageError("Can't use statement directly after '%%time'!")
|
1336 |
+
|
1337 |
+
if cell:
|
1338 |
+
expr = self.shell.transform_cell(cell)
|
1339 |
+
else:
|
1340 |
+
expr = self.shell.transform_cell(line)
|
1341 |
+
|
1342 |
+
# Minimum time above which parse time will be reported
|
1343 |
+
tp_min = 0.1
|
1344 |
+
|
1345 |
+
t0 = clock()
|
1346 |
+
expr_ast = self.shell.compile.ast_parse(expr)
|
1347 |
+
tp = clock() - t0
|
1348 |
+
|
1349 |
+
# Apply AST transformations
|
1350 |
+
expr_ast = self.shell.transform_ast(expr_ast)
|
1351 |
+
|
1352 |
+
# Minimum time above which compilation time will be reported
|
1353 |
+
tc_min = 0.1
|
1354 |
+
|
1355 |
+
expr_val = None
|
1356 |
+
if len(expr_ast.body) == 1 and isinstance(expr_ast.body[0], ast.Expr):
|
1357 |
+
mode = 'eval'
|
1358 |
+
source = '<timed eval>'
|
1359 |
+
expr_ast = ast.Expression(expr_ast.body[0].value)
|
1360 |
+
else:
|
1361 |
+
mode = 'exec'
|
1362 |
+
source = '<timed exec>'
|
1363 |
+
# multi-line %%time case
|
1364 |
+
if len(expr_ast.body) > 1 and isinstance(expr_ast.body[-1], ast.Expr):
|
1365 |
+
expr_val = expr_ast.body[-1]
|
1366 |
+
expr_ast = expr_ast.body[:-1]
|
1367 |
+
expr_ast = Module(expr_ast, [])
|
1368 |
+
expr_val = ast.Expression(expr_val.value)
|
1369 |
+
|
1370 |
+
t0 = clock()
|
1371 |
+
code = self.shell.compile(expr_ast, source, mode)
|
1372 |
+
tc = clock() - t0
|
1373 |
+
|
1374 |
+
# skew measurement as little as possible
|
1375 |
+
glob = self.shell.user_ns
|
1376 |
+
wtime = time.time
|
1377 |
+
# time execution
|
1378 |
+
wall_st = wtime()
|
1379 |
+
if mode == "eval":
|
1380 |
+
st = clock2()
|
1381 |
+
try:
|
1382 |
+
out = eval(code, glob, local_ns)
|
1383 |
+
except Exception:
|
1384 |
+
self.shell.showtraceback()
|
1385 |
+
return
|
1386 |
+
end = clock2()
|
1387 |
+
else:
|
1388 |
+
st = clock2()
|
1389 |
+
try:
|
1390 |
+
exec(code, glob, local_ns)
|
1391 |
+
out = None
|
1392 |
+
# multi-line %%time case
|
1393 |
+
if expr_val is not None:
|
1394 |
+
code_2 = self.shell.compile(expr_val, source, 'eval')
|
1395 |
+
out = eval(code_2, glob, local_ns)
|
1396 |
+
except Exception:
|
1397 |
+
self.shell.showtraceback()
|
1398 |
+
return
|
1399 |
+
end = clock2()
|
1400 |
+
|
1401 |
+
wall_end = wtime()
|
1402 |
+
# Compute actual times and report
|
1403 |
+
wall_time = wall_end - wall_st
|
1404 |
+
cpu_user = end[0] - st[0]
|
1405 |
+
cpu_sys = end[1] - st[1]
|
1406 |
+
cpu_tot = cpu_user + cpu_sys
|
1407 |
+
# On windows cpu_sys is always zero, so only total is displayed
|
1408 |
+
if sys.platform != "win32":
|
1409 |
+
print(
|
1410 |
+
f"CPU times: user {_format_time(cpu_user)}, sys: {_format_time(cpu_sys)}, total: {_format_time(cpu_tot)}"
|
1411 |
+
)
|
1412 |
+
else:
|
1413 |
+
print(f"CPU times: total: {_format_time(cpu_tot)}")
|
1414 |
+
print(f"Wall time: {_format_time(wall_time)}")
|
1415 |
+
if tc > tc_min:
|
1416 |
+
print(f"Compiler : {_format_time(tc)}")
|
1417 |
+
if tp > tp_min:
|
1418 |
+
print(f"Parser : {_format_time(tp)}")
|
1419 |
+
return out
|
1420 |
+
|
1421 |
+
@skip_doctest
|
1422 |
+
@line_magic
|
1423 |
+
def macro(self, parameter_s=''):
|
1424 |
+
"""Define a macro for future re-execution. It accepts ranges of history,
|
1425 |
+
filenames or string objects.
|
1426 |
+
|
1427 |
+
Usage::
|
1428 |
+
|
1429 |
+
%macro [options] name n1-n2 n3-n4 ... n5 .. n6 ...
|
1430 |
+
|
1431 |
+
Options:
|
1432 |
+
|
1433 |
+
-r
|
1434 |
+
Use 'raw' input. By default, the 'processed' history is used,
|
1435 |
+
so that magics are loaded in their transformed version to valid
|
1436 |
+
Python. If this option is given, the raw input as typed at the
|
1437 |
+
command line is used instead.
|
1438 |
+
|
1439 |
+
-q
|
1440 |
+
Quiet macro definition. By default, a tag line is printed
|
1441 |
+
to indicate the macro has been created, and then the contents of
|
1442 |
+
the macro are printed. If this option is given, then no printout
|
1443 |
+
is produced once the macro is created.
|
1444 |
+
|
1445 |
+
This will define a global variable called `name` which is a string
|
1446 |
+
made of joining the slices and lines you specify (n1,n2,... numbers
|
1447 |
+
above) from your input history into a single string. This variable
|
1448 |
+
acts like an automatic function which re-executes those lines as if
|
1449 |
+
you had typed them. You just type 'name' at the prompt and the code
|
1450 |
+
executes.
|
1451 |
+
|
1452 |
+
The syntax for indicating input ranges is described in %history.
|
1453 |
+
|
1454 |
+
Note: as a 'hidden' feature, you can also use traditional python slice
|
1455 |
+
notation, where N:M means numbers N through M-1.
|
1456 |
+
|
1457 |
+
For example, if your history contains (print using %hist -n )::
|
1458 |
+
|
1459 |
+
44: x=1
|
1460 |
+
45: y=3
|
1461 |
+
46: z=x+y
|
1462 |
+
47: print(x)
|
1463 |
+
48: a=5
|
1464 |
+
49: print('x',x,'y',y)
|
1465 |
+
|
1466 |
+
you can create a macro with lines 44 through 47 (included) and line 49
|
1467 |
+
called my_macro with::
|
1468 |
+
|
1469 |
+
In [55]: %macro my_macro 44-47 49
|
1470 |
+
|
1471 |
+
Now, typing `my_macro` (without quotes) will re-execute all this code
|
1472 |
+
in one pass.
|
1473 |
+
|
1474 |
+
You don't need to give the line-numbers in order, and any given line
|
1475 |
+
number can appear multiple times. You can assemble macros with any
|
1476 |
+
lines from your input history in any order.
|
1477 |
+
|
1478 |
+
The macro is a simple object which holds its value in an attribute,
|
1479 |
+
but IPython's display system checks for macros and executes them as
|
1480 |
+
code instead of printing them when you type their name.
|
1481 |
+
|
1482 |
+
You can view a macro's contents by explicitly printing it with::
|
1483 |
+
|
1484 |
+
print(macro_name)
|
1485 |
+
|
1486 |
+
"""
|
1487 |
+
opts,args = self.parse_options(parameter_s,'rq',mode='list')
|
1488 |
+
if not args: # List existing macros
|
1489 |
+
return sorted(k for k,v in self.shell.user_ns.items() if isinstance(v, Macro))
|
1490 |
+
if len(args) == 1:
|
1491 |
+
raise UsageError(
|
1492 |
+
"%macro insufficient args; usage '%macro name n1-n2 n3-4...")
|
1493 |
+
name, codefrom = args[0], " ".join(args[1:])
|
1494 |
+
|
1495 |
+
# print('rng',ranges) # dbg
|
1496 |
+
try:
|
1497 |
+
lines = self.shell.find_user_code(codefrom, 'r' in opts)
|
1498 |
+
except (ValueError, TypeError) as e:
|
1499 |
+
print(e.args[0])
|
1500 |
+
return
|
1501 |
+
macro = Macro(lines)
|
1502 |
+
self.shell.define_macro(name, macro)
|
1503 |
+
if "q" not in opts:
|
1504 |
+
print(
|
1505 |
+
"Macro `%s` created. To execute, type its name (without quotes)." % name
|
1506 |
+
)
|
1507 |
+
print("=== Macro contents: ===")
|
1508 |
+
print(macro, end=" ")
|
1509 |
+
|
1510 |
+
@magic_arguments.magic_arguments()
|
1511 |
+
@magic_arguments.argument(
|
1512 |
+
"output",
|
1513 |
+
type=str,
|
1514 |
+
default="",
|
1515 |
+
nargs="?",
|
1516 |
+
help="""
|
1517 |
+
|
1518 |
+
The name of the variable in which to store output.
|
1519 |
+
This is a ``utils.io.CapturedIO`` object with stdout/err attributes
|
1520 |
+
for the text of the captured output.
|
1521 |
+
|
1522 |
+
CapturedOutput also has a ``show()`` method for displaying the output,
|
1523 |
+
and ``__call__`` as well, so you can use that to quickly display the
|
1524 |
+
output.
|
1525 |
+
|
1526 |
+
If unspecified, captured output is discarded.
|
1527 |
+
""",
|
1528 |
+
)
|
1529 |
+
@magic_arguments.argument(
|
1530 |
+
"--no-stderr", action="store_true", help="""Don't capture stderr."""
|
1531 |
+
)
|
1532 |
+
@magic_arguments.argument(
|
1533 |
+
"--no-stdout", action="store_true", help="""Don't capture stdout."""
|
1534 |
+
)
|
1535 |
+
@magic_arguments.argument(
|
1536 |
+
"--no-display",
|
1537 |
+
action="store_true",
|
1538 |
+
help="""Don't capture IPython's rich display."""
|
1539 |
+
)
|
1540 |
+
@cell_magic
|
1541 |
+
def capture(self, line, cell):
|
1542 |
+
"""run the cell, capturing stdout, stderr, and IPython's rich display() calls."""
|
1543 |
+
args = magic_arguments.parse_argstring(self.capture, line)
|
1544 |
+
out = not args.no_stdout
|
1545 |
+
err = not args.no_stderr
|
1546 |
+
disp = not args.no_display
|
1547 |
+
with capture_output(out, err, disp) as io:
|
1548 |
+
self.shell.run_cell(cell)
|
1549 |
+
if DisplayHook.semicolon_at_end_of_expression(cell):
|
1550 |
+
if args.output in self.shell.user_ns:
|
1551 |
+
del self.shell.user_ns[args.output]
|
1552 |
+
elif args.output:
|
1553 |
+
self.shell.user_ns[args.output] = io
|
1554 |
+
|
1555 |
+
@skip_doctest
|
1556 |
+
@magic_arguments.magic_arguments()
|
1557 |
+
@magic_arguments.argument("name", type=str, default="default", nargs="?")
|
1558 |
+
@magic_arguments.argument(
|
1559 |
+
"--remove", action="store_true", help="remove the current transformer"
|
1560 |
+
)
|
1561 |
+
@magic_arguments.argument(
|
1562 |
+
"--list", action="store_true", help="list existing transformers name"
|
1563 |
+
)
|
1564 |
+
@magic_arguments.argument(
|
1565 |
+
"--list-all",
|
1566 |
+
action="store_true",
|
1567 |
+
help="list existing transformers name and code template",
|
1568 |
+
)
|
1569 |
+
@line_cell_magic
|
1570 |
+
def code_wrap(self, line, cell=None):
|
1571 |
+
"""
|
1572 |
+
Simple magic to quickly define a code transformer for all IPython's future input.
|
1573 |
+
|
1574 |
+
``__code__`` and ``__ret__`` are special variable that represent the code to run
|
1575 |
+
and the value of the last expression of ``__code__`` respectively.
|
1576 |
+
|
1577 |
+
Examples
|
1578 |
+
--------
|
1579 |
+
|
1580 |
+
.. ipython::
|
1581 |
+
|
1582 |
+
In [1]: %%code_wrap before_after
|
1583 |
+
...: print('before')
|
1584 |
+
...: __code__
|
1585 |
+
...: print('after')
|
1586 |
+
...: __ret__
|
1587 |
+
|
1588 |
+
|
1589 |
+
In [2]: 1
|
1590 |
+
before
|
1591 |
+
after
|
1592 |
+
Out[2]: 1
|
1593 |
+
|
1594 |
+
In [3]: %code_wrap --list
|
1595 |
+
before_after
|
1596 |
+
|
1597 |
+
In [4]: %code_wrap --list-all
|
1598 |
+
before_after :
|
1599 |
+
print('before')
|
1600 |
+
__code__
|
1601 |
+
print('after')
|
1602 |
+
__ret__
|
1603 |
+
|
1604 |
+
In [5]: %code_wrap --remove before_after
|
1605 |
+
|
1606 |
+
"""
|
1607 |
+
args = magic_arguments.parse_argstring(self.code_wrap, line)
|
1608 |
+
|
1609 |
+
if args.list:
|
1610 |
+
for name in self._transformers.keys():
|
1611 |
+
print(name)
|
1612 |
+
return
|
1613 |
+
if args.list_all:
|
1614 |
+
for name, _t in self._transformers.items():
|
1615 |
+
print(name, ":")
|
1616 |
+
print(indent(ast.unparse(_t.template), " "))
|
1617 |
+
print()
|
1618 |
+
return
|
1619 |
+
|
1620 |
+
to_remove = self._transformers.pop(args.name, None)
|
1621 |
+
if to_remove in self.shell.ast_transformers:
|
1622 |
+
self.shell.ast_transformers.remove(to_remove)
|
1623 |
+
if cell is None or args.remove:
|
1624 |
+
return
|
1625 |
+
|
1626 |
+
_trs = ReplaceCodeTransformer(ast.parse(cell))
|
1627 |
+
|
1628 |
+
self._transformers[args.name] = _trs
|
1629 |
+
self.shell.ast_transformers.append(_trs)
|
1630 |
+
|
1631 |
+
|
1632 |
+
def parse_breakpoint(text, current_file):
|
1633 |
+
'''Returns (file, line) for file:line and (current_file, line) for line'''
|
1634 |
+
colon = text.find(':')
|
1635 |
+
if colon == -1:
|
1636 |
+
return current_file, int(text)
|
1637 |
+
else:
|
1638 |
+
return text[:colon], int(text[colon+1:])
|
1639 |
+
|
1640 |
+
|
1641 |
+
def _format_time(timespan, precision=3):
|
1642 |
+
"""Formats the timespan in a human readable form"""
|
1643 |
+
|
1644 |
+
if timespan >= 60.0:
|
1645 |
+
# we have more than a minute, format that in a human readable form
|
1646 |
+
# Idea from http://snipplr.com/view/5713/
|
1647 |
+
parts = [("d", 60 * 60 * 24), ("h", 60 * 60), ("min", 60), ("s", 1)]
|
1648 |
+
time = []
|
1649 |
+
leftover = timespan
|
1650 |
+
for suffix, length in parts:
|
1651 |
+
value = int(leftover / length)
|
1652 |
+
if value > 0:
|
1653 |
+
leftover = leftover % length
|
1654 |
+
time.append("%s%s" % (str(value), suffix))
|
1655 |
+
if leftover < 1:
|
1656 |
+
break
|
1657 |
+
return " ".join(time)
|
1658 |
+
|
1659 |
+
# Unfortunately characters outside of range(128) can cause problems in
|
1660 |
+
# certain terminals.
|
1661 |
+
# See bug: https://bugs.launchpad.net/ipython/+bug/348466
|
1662 |
+
# Try to prevent crashes by being more secure than it needs to
|
1663 |
+
# E.g. eclipse is able to print a µ, but has no sys.stdout.encoding set.
|
1664 |
+
units = ["s", "ms", "us", "ns"] # the safe value
|
1665 |
+
if hasattr(sys.stdout, "encoding") and sys.stdout.encoding:
|
1666 |
+
try:
|
1667 |
+
"μ".encode(sys.stdout.encoding)
|
1668 |
+
units = ["s", "ms", "μs", "ns"]
|
1669 |
+
except:
|
1670 |
+
pass
|
1671 |
+
scaling = [1, 1e3, 1e6, 1e9]
|
1672 |
+
|
1673 |
+
if timespan > 0.0:
|
1674 |
+
order = min(-int(math.floor(math.log10(timespan)) // 3), 3)
|
1675 |
+
else:
|
1676 |
+
order = 3
|
1677 |
+
return "%.*g %s" % (precision, timespan * scaling[order], units[order])
|
temp_venv/lib/python3.13/site-packages/IPython/core/magics/extension.py
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Implementation of magic functions for the extension machinery.
|
2 |
+
"""
|
3 |
+
#-----------------------------------------------------------------------------
|
4 |
+
# Copyright (c) 2012 The IPython Development Team.
|
5 |
+
#
|
6 |
+
# Distributed under the terms of the Modified BSD License.
|
7 |
+
#
|
8 |
+
# The full license is in the file COPYING.txt, distributed with this software.
|
9 |
+
#-----------------------------------------------------------------------------
|
10 |
+
|
11 |
+
#-----------------------------------------------------------------------------
|
12 |
+
# Imports
|
13 |
+
#-----------------------------------------------------------------------------
|
14 |
+
|
15 |
+
|
16 |
+
# Our own packages
|
17 |
+
from IPython.core.error import UsageError
|
18 |
+
from IPython.core.magic import Magics, magics_class, line_magic
|
19 |
+
|
20 |
+
#-----------------------------------------------------------------------------
|
21 |
+
# Magic implementation classes
|
22 |
+
#-----------------------------------------------------------------------------
|
23 |
+
|
24 |
+
@magics_class
|
25 |
+
class ExtensionMagics(Magics):
|
26 |
+
"""Magics to manage the IPython extensions system."""
|
27 |
+
|
28 |
+
@line_magic
|
29 |
+
def load_ext(self, module_str):
|
30 |
+
"""Load an IPython extension by its module name."""
|
31 |
+
if not module_str:
|
32 |
+
raise UsageError('Missing module name.')
|
33 |
+
res = self.shell.extension_manager.load_extension(module_str)
|
34 |
+
|
35 |
+
if res == 'already loaded':
|
36 |
+
print("The %s extension is already loaded. To reload it, use:" % module_str)
|
37 |
+
print(" %reload_ext", module_str)
|
38 |
+
elif res == 'no load function':
|
39 |
+
print("The %s module is not an IPython extension." % module_str)
|
40 |
+
|
41 |
+
@line_magic
|
42 |
+
def unload_ext(self, module_str):
|
43 |
+
"""Unload an IPython extension by its module name.
|
44 |
+
|
45 |
+
Not all extensions can be unloaded, only those which define an
|
46 |
+
``unload_ipython_extension`` function.
|
47 |
+
"""
|
48 |
+
if not module_str:
|
49 |
+
raise UsageError('Missing module name.')
|
50 |
+
|
51 |
+
res = self.shell.extension_manager.unload_extension(module_str)
|
52 |
+
|
53 |
+
if res == 'no unload function':
|
54 |
+
print("The %s extension doesn't define how to unload it." % module_str)
|
55 |
+
elif res == "not loaded":
|
56 |
+
print("The %s extension is not loaded." % module_str)
|
57 |
+
|
58 |
+
@line_magic
|
59 |
+
def reload_ext(self, module_str):
|
60 |
+
"""Reload an IPython extension by its module name."""
|
61 |
+
if not module_str:
|
62 |
+
raise UsageError('Missing module name.')
|
63 |
+
self.shell.extension_manager.reload_extension(module_str)
|
temp_venv/lib/python3.13/site-packages/IPython/core/magics/history.py
ADDED
@@ -0,0 +1,338 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Implementation of magic functions related to History.
|
2 |
+
"""
|
3 |
+
#-----------------------------------------------------------------------------
|
4 |
+
# Copyright (c) 2012, IPython Development Team.
|
5 |
+
#
|
6 |
+
# Distributed under the terms of the Modified BSD License.
|
7 |
+
#
|
8 |
+
# The full license is in the file COPYING.txt, distributed with this software.
|
9 |
+
#-----------------------------------------------------------------------------
|
10 |
+
|
11 |
+
#-----------------------------------------------------------------------------
|
12 |
+
# Imports
|
13 |
+
#-----------------------------------------------------------------------------
|
14 |
+
|
15 |
+
# Stdlib
|
16 |
+
import os
|
17 |
+
import sys
|
18 |
+
from io import open as io_open
|
19 |
+
import fnmatch
|
20 |
+
|
21 |
+
# Our own packages
|
22 |
+
from IPython.core.error import StdinNotImplementedError
|
23 |
+
from IPython.core.magic import Magics, magics_class, line_magic
|
24 |
+
from IPython.core.magic_arguments import (argument, magic_arguments,
|
25 |
+
parse_argstring)
|
26 |
+
from IPython.testing.skipdoctest import skip_doctest
|
27 |
+
from IPython.utils import io
|
28 |
+
|
29 |
+
#-----------------------------------------------------------------------------
|
30 |
+
# Magics class implementation
|
31 |
+
#-----------------------------------------------------------------------------
|
32 |
+
|
33 |
+
|
34 |
+
_unspecified = object()
|
35 |
+
|
36 |
+
|
37 |
+
@magics_class
|
38 |
+
class HistoryMagics(Magics):
|
39 |
+
|
40 |
+
@magic_arguments()
|
41 |
+
@argument(
|
42 |
+
'-n', dest='print_nums', action='store_true', default=False,
|
43 |
+
help="""
|
44 |
+
print line numbers for each input.
|
45 |
+
This feature is only available if numbered prompts are in use.
|
46 |
+
""")
|
47 |
+
@argument(
|
48 |
+
'-o', dest='get_output', action='store_true', default=False,
|
49 |
+
help="also print outputs for each input.")
|
50 |
+
@argument(
|
51 |
+
'-p', dest='pyprompts', action='store_true', default=False,
|
52 |
+
help="""
|
53 |
+
print classic '>>>' python prompts before each input.
|
54 |
+
This is useful for making documentation, and in conjunction
|
55 |
+
with -o, for producing doctest-ready output.
|
56 |
+
""")
|
57 |
+
@argument(
|
58 |
+
'-t', dest='raw', action='store_false', default=True,
|
59 |
+
help="""
|
60 |
+
print the 'translated' history, as IPython understands it.
|
61 |
+
IPython filters your input and converts it all into valid Python
|
62 |
+
source before executing it (things like magics or aliases are turned
|
63 |
+
into function calls, for example). With this option, you'll see the
|
64 |
+
native history instead of the user-entered version: '%%cd /' will be
|
65 |
+
seen as 'get_ipython().run_line_magic("cd", "/")' instead of '%%cd /'.
|
66 |
+
""")
|
67 |
+
@argument(
|
68 |
+
'-f', dest='filename',
|
69 |
+
help="""
|
70 |
+
FILENAME: instead of printing the output to the screen, redirect
|
71 |
+
it to the given file. The file is always overwritten, though *when
|
72 |
+
it can*, IPython asks for confirmation first. In particular, running
|
73 |
+
the command 'history -f FILENAME' from the IPython Notebook
|
74 |
+
interface will replace FILENAME even if it already exists *without*
|
75 |
+
confirmation.
|
76 |
+
""")
|
77 |
+
@argument(
|
78 |
+
'-g', dest='pattern', nargs='*', default=None,
|
79 |
+
help="""
|
80 |
+
treat the arg as a glob pattern to search for in (full) history.
|
81 |
+
This includes the saved history (almost all commands ever written).
|
82 |
+
The pattern may contain '?' to match one unknown character and '*'
|
83 |
+
to match any number of unknown characters. Use '%%hist -g' to show
|
84 |
+
full saved history (may be very long).
|
85 |
+
""")
|
86 |
+
@argument(
|
87 |
+
'-l', dest='limit', type=int, nargs='?', default=_unspecified,
|
88 |
+
help="""
|
89 |
+
get the last n lines from all sessions. Specify n as a single
|
90 |
+
arg, or the default is the last 10 lines.
|
91 |
+
""")
|
92 |
+
@argument(
|
93 |
+
'-u', dest='unique', action='store_true',
|
94 |
+
help="""
|
95 |
+
when searching history using `-g`, show only unique history.
|
96 |
+
""")
|
97 |
+
@argument('range', nargs='*')
|
98 |
+
@skip_doctest
|
99 |
+
@line_magic
|
100 |
+
def history(self, parameter_s = ''):
|
101 |
+
"""Print input history (_i<n> variables), with most recent last.
|
102 |
+
|
103 |
+
By default, input history is printed without line numbers so it can be
|
104 |
+
directly pasted into an editor. Use -n to show them.
|
105 |
+
|
106 |
+
By default, all input history from the current session is displayed.
|
107 |
+
Ranges of history can be indicated using the syntax:
|
108 |
+
|
109 |
+
``4``
|
110 |
+
Line 4, current session
|
111 |
+
``4-6``
|
112 |
+
Lines 4-6, current session
|
113 |
+
``243/1-5``
|
114 |
+
Lines 1-5, session 243
|
115 |
+
``~2/7``
|
116 |
+
Line 7, session 2 before current
|
117 |
+
``~8/1-~6/5``
|
118 |
+
From the first line of 8 sessions ago, to the fifth line of 6
|
119 |
+
sessions ago.
|
120 |
+
|
121 |
+
Multiple ranges can be entered, separated by spaces
|
122 |
+
|
123 |
+
The same syntax is used by %macro, %save, %edit, %rerun
|
124 |
+
|
125 |
+
Examples
|
126 |
+
--------
|
127 |
+
::
|
128 |
+
|
129 |
+
In [6]: %history -n 4-6
|
130 |
+
4:a = 12
|
131 |
+
5:print(a**2)
|
132 |
+
6:%history -n 4-6
|
133 |
+
|
134 |
+
"""
|
135 |
+
|
136 |
+
args = parse_argstring(self.history, parameter_s)
|
137 |
+
|
138 |
+
# For brevity
|
139 |
+
history_manager = self.shell.history_manager
|
140 |
+
|
141 |
+
def _format_lineno(session, line):
|
142 |
+
"""Helper function to format line numbers properly."""
|
143 |
+
if session in (0, history_manager.session_number):
|
144 |
+
return str(line)
|
145 |
+
return "%s/%s" % (session, line)
|
146 |
+
|
147 |
+
# Check if output to specific file was requested.
|
148 |
+
outfname = args.filename
|
149 |
+
if not outfname:
|
150 |
+
outfile = sys.stdout # default
|
151 |
+
# We don't want to close stdout at the end!
|
152 |
+
close_at_end = False
|
153 |
+
else:
|
154 |
+
outfname = os.path.expanduser(outfname)
|
155 |
+
if os.path.exists(outfname):
|
156 |
+
try:
|
157 |
+
ans = io.ask_yes_no("File %r exists. Overwrite?" % outfname)
|
158 |
+
except StdinNotImplementedError:
|
159 |
+
ans = True
|
160 |
+
if not ans:
|
161 |
+
print('Aborting.')
|
162 |
+
return
|
163 |
+
print("Overwriting file.")
|
164 |
+
outfile = io_open(outfname, 'w', encoding='utf-8')
|
165 |
+
close_at_end = True
|
166 |
+
|
167 |
+
print_nums = args.print_nums
|
168 |
+
get_output = args.get_output
|
169 |
+
pyprompts = args.pyprompts
|
170 |
+
raw = args.raw
|
171 |
+
|
172 |
+
pattern = None
|
173 |
+
limit = None if args.limit is _unspecified else args.limit
|
174 |
+
|
175 |
+
range_pattern = False
|
176 |
+
if args.pattern is not None and not args.range:
|
177 |
+
if args.pattern:
|
178 |
+
pattern = "*" + " ".join(args.pattern) + "*"
|
179 |
+
else:
|
180 |
+
pattern = "*"
|
181 |
+
hist = history_manager.search(pattern, raw=raw, output=get_output,
|
182 |
+
n=limit, unique=args.unique)
|
183 |
+
print_nums = True
|
184 |
+
elif args.limit is not _unspecified:
|
185 |
+
n = 10 if limit is None else limit
|
186 |
+
hist = history_manager.get_tail(n, raw=raw, output=get_output)
|
187 |
+
else:
|
188 |
+
if args.pattern:
|
189 |
+
range_pattern = "*" + " ".join(args.pattern) + "*"
|
190 |
+
print_nums = True
|
191 |
+
hist = history_manager.get_range_by_str(
|
192 |
+
" ".join(args.range), raw, get_output
|
193 |
+
)
|
194 |
+
|
195 |
+
# We could be displaying the entire history, so let's not try to pull
|
196 |
+
# it into a list in memory. Anything that needs more space will just
|
197 |
+
# misalign.
|
198 |
+
width = 4
|
199 |
+
|
200 |
+
for session, lineno, inline in hist:
|
201 |
+
# Print user history with tabs expanded to 4 spaces. The GUI
|
202 |
+
# clients use hard tabs for easier usability in auto-indented code,
|
203 |
+
# but we want to produce PEP-8 compliant history for safe pasting
|
204 |
+
# into an editor.
|
205 |
+
if get_output:
|
206 |
+
inline, output = inline
|
207 |
+
if range_pattern:
|
208 |
+
if not fnmatch.fnmatch(inline, range_pattern):
|
209 |
+
continue
|
210 |
+
inline = inline.expandtabs(4).rstrip()
|
211 |
+
|
212 |
+
multiline = "\n" in inline
|
213 |
+
line_sep = '\n' if multiline else ' '
|
214 |
+
if print_nums:
|
215 |
+
print(u'%s:%s' % (_format_lineno(session, lineno).rjust(width),
|
216 |
+
line_sep), file=outfile, end=u'')
|
217 |
+
if pyprompts:
|
218 |
+
print(u">>> ", end=u"", file=outfile)
|
219 |
+
if multiline:
|
220 |
+
inline = "\n... ".join(inline.splitlines()) + "\n..."
|
221 |
+
print(inline, file=outfile)
|
222 |
+
if get_output and output:
|
223 |
+
print(output, file=outfile)
|
224 |
+
|
225 |
+
if close_at_end:
|
226 |
+
outfile.close()
|
227 |
+
|
228 |
+
@line_magic
|
229 |
+
def recall(self, arg):
|
230 |
+
r"""Repeat a command, or get command to input line for editing.
|
231 |
+
|
232 |
+
%recall and %rep are equivalent.
|
233 |
+
|
234 |
+
- %recall (no arguments):
|
235 |
+
|
236 |
+
Place a string version of last computation result (stored in the
|
237 |
+
special '_' variable) to the next input prompt. Allows you to create
|
238 |
+
elaborate command lines without using copy-paste::
|
239 |
+
|
240 |
+
In[1]: l = ["hei", "vaan"]
|
241 |
+
In[2]: "".join(l)
|
242 |
+
Out[2]: heivaan
|
243 |
+
In[3]: %recall
|
244 |
+
In[4]: heivaan_ <== cursor blinking
|
245 |
+
|
246 |
+
%recall 45
|
247 |
+
|
248 |
+
Place history line 45 on the next input prompt. Use %hist to find
|
249 |
+
out the number.
|
250 |
+
|
251 |
+
%recall 1-4
|
252 |
+
|
253 |
+
Combine the specified lines into one cell, and place it on the next
|
254 |
+
input prompt. See %history for the slice syntax.
|
255 |
+
|
256 |
+
%recall foo+bar
|
257 |
+
|
258 |
+
If foo+bar can be evaluated in the user namespace, the result is
|
259 |
+
placed at the next input prompt. Otherwise, the history is searched
|
260 |
+
for lines which contain that substring, and the most recent one is
|
261 |
+
placed at the next input prompt.
|
262 |
+
"""
|
263 |
+
if not arg: # Last output
|
264 |
+
self.shell.set_next_input(str(self.shell.user_ns["_"]))
|
265 |
+
return
|
266 |
+
# Get history range
|
267 |
+
histlines = self.shell.history_manager.get_range_by_str(arg)
|
268 |
+
cmd = "\n".join(x[2] for x in histlines)
|
269 |
+
if cmd:
|
270 |
+
self.shell.set_next_input(cmd.rstrip())
|
271 |
+
return
|
272 |
+
|
273 |
+
try: # Variable in user namespace
|
274 |
+
cmd = str(eval(arg, self.shell.user_ns))
|
275 |
+
except Exception: # Search for term in history
|
276 |
+
histlines = self.shell.history_manager.search("*"+arg+"*")
|
277 |
+
for h in reversed([x[2] for x in histlines]):
|
278 |
+
if 'recall' in h or 'rep' in h:
|
279 |
+
continue
|
280 |
+
self.shell.set_next_input(h.rstrip())
|
281 |
+
return
|
282 |
+
else:
|
283 |
+
self.shell.set_next_input(cmd.rstrip())
|
284 |
+
return
|
285 |
+
print("Couldn't evaluate or find in history:", arg)
|
286 |
+
|
287 |
+
@line_magic
|
288 |
+
def rerun(self, parameter_s=''):
|
289 |
+
"""Re-run previous input
|
290 |
+
|
291 |
+
By default, you can specify ranges of input history to be repeated
|
292 |
+
(as with %history). With no arguments, it will repeat the last line.
|
293 |
+
|
294 |
+
Options:
|
295 |
+
|
296 |
+
-l <n> : Repeat the last n lines of input, not including the
|
297 |
+
current command.
|
298 |
+
|
299 |
+
-g foo : Repeat the most recent line which contains foo
|
300 |
+
"""
|
301 |
+
opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
|
302 |
+
if "l" in opts: # Last n lines
|
303 |
+
try:
|
304 |
+
n = int(opts["l"])
|
305 |
+
except ValueError:
|
306 |
+
print("Number of lines must be an integer")
|
307 |
+
return
|
308 |
+
|
309 |
+
if n == 0:
|
310 |
+
print("Requested 0 last lines - nothing to run")
|
311 |
+
return
|
312 |
+
elif n < 0:
|
313 |
+
print("Number of lines to rerun cannot be negative")
|
314 |
+
return
|
315 |
+
|
316 |
+
hist = self.shell.history_manager.get_tail(n)
|
317 |
+
elif "g" in opts: # Search
|
318 |
+
p = "*"+opts['g']+"*"
|
319 |
+
hist = list(self.shell.history_manager.search(p))
|
320 |
+
for l in reversed(hist):
|
321 |
+
if "rerun" not in l[2]:
|
322 |
+
hist = [l] # The last match which isn't a %rerun
|
323 |
+
break
|
324 |
+
else:
|
325 |
+
hist = [] # No matches except %rerun
|
326 |
+
elif args: # Specify history ranges
|
327 |
+
hist = self.shell.history_manager.get_range_by_str(args)
|
328 |
+
else: # Last line
|
329 |
+
hist = self.shell.history_manager.get_tail(1)
|
330 |
+
hist = [x[2] for x in hist]
|
331 |
+
if not hist:
|
332 |
+
print("No lines in history match specification")
|
333 |
+
return
|
334 |
+
histlines = "\n".join(hist)
|
335 |
+
print("=== Executing: ===")
|
336 |
+
print(histlines)
|
337 |
+
print("=== Output: ===")
|
338 |
+
self.shell.run_cell("\n".join(hist), store_history=False)
|
temp_venv/lib/python3.13/site-packages/IPython/core/magics/logging.py
ADDED
@@ -0,0 +1,195 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Implementation of magic functions for IPython's own logging.
|
2 |
+
"""
|
3 |
+
#-----------------------------------------------------------------------------
|
4 |
+
# Copyright (c) 2012 The IPython Development Team.
|
5 |
+
#
|
6 |
+
# Distributed under the terms of the Modified BSD License.
|
7 |
+
#
|
8 |
+
# The full license is in the file COPYING.txt, distributed with this software.
|
9 |
+
#-----------------------------------------------------------------------------
|
10 |
+
|
11 |
+
#-----------------------------------------------------------------------------
|
12 |
+
# Imports
|
13 |
+
#-----------------------------------------------------------------------------
|
14 |
+
|
15 |
+
# Stdlib
|
16 |
+
import os
|
17 |
+
import sys
|
18 |
+
|
19 |
+
# Our own packages
|
20 |
+
from IPython.core.magic import Magics, magics_class, line_magic
|
21 |
+
from warnings import warn
|
22 |
+
from traitlets import Bool
|
23 |
+
|
24 |
+
#-----------------------------------------------------------------------------
|
25 |
+
# Magic implementation classes
|
26 |
+
#-----------------------------------------------------------------------------
|
27 |
+
|
28 |
+
@magics_class
|
29 |
+
class LoggingMagics(Magics):
|
30 |
+
"""Magics related to all logging machinery."""
|
31 |
+
|
32 |
+
quiet = Bool(False, help=
|
33 |
+
"""
|
34 |
+
Suppress output of log state when logging is enabled
|
35 |
+
"""
|
36 |
+
).tag(config=True)
|
37 |
+
|
38 |
+
@line_magic
|
39 |
+
def logstart(self, parameter_s=''):
|
40 |
+
"""Start logging anywhere in a session.
|
41 |
+
|
42 |
+
%logstart [-o|-r|-t|-q] [log_name [log_mode]]
|
43 |
+
|
44 |
+
If no name is given, it defaults to a file named 'ipython_log.py' in your
|
45 |
+
current directory, in 'rotate' mode (see below).
|
46 |
+
|
47 |
+
'%logstart name' saves to file 'name' in 'backup' mode. It saves your
|
48 |
+
history up to that point and then continues logging.
|
49 |
+
|
50 |
+
%logstart takes a second optional parameter: logging mode. This can be one
|
51 |
+
of (note that the modes are given unquoted):
|
52 |
+
|
53 |
+
append
|
54 |
+
Keep logging at the end of any existing file.
|
55 |
+
|
56 |
+
backup
|
57 |
+
Rename any existing file to name~ and start name.
|
58 |
+
|
59 |
+
global
|
60 |
+
Append to a single logfile in your home directory.
|
61 |
+
|
62 |
+
over
|
63 |
+
Overwrite any existing log.
|
64 |
+
|
65 |
+
rotate
|
66 |
+
Create rotating logs: name.1~, name.2~, etc.
|
67 |
+
|
68 |
+
Options:
|
69 |
+
|
70 |
+
-o
|
71 |
+
log also IPython's output. In this mode, all commands which
|
72 |
+
generate an Out[NN] prompt are recorded to the logfile, right after
|
73 |
+
their corresponding input line. The output lines are always
|
74 |
+
prepended with a '#[Out]# ' marker, so that the log remains valid
|
75 |
+
Python code.
|
76 |
+
|
77 |
+
Since this marker is always the same, filtering only the output from
|
78 |
+
a log is very easy, using for example a simple awk call::
|
79 |
+
|
80 |
+
awk -F'#\\[Out\\]# ' '{if($2) {print $2}}' ipython_log.py
|
81 |
+
|
82 |
+
-r
|
83 |
+
log 'raw' input. Normally, IPython's logs contain the processed
|
84 |
+
input, so that user lines are logged in their final form, converted
|
85 |
+
into valid Python. For example, %Exit is logged as
|
86 |
+
_ip.run_line_magic("Exit"). If the -r flag is given, all input is logged
|
87 |
+
exactly as typed, with no transformations applied.
|
88 |
+
|
89 |
+
-t
|
90 |
+
put timestamps before each input line logged (these are put in
|
91 |
+
comments).
|
92 |
+
|
93 |
+
-q
|
94 |
+
suppress output of logstate message when logging is invoked
|
95 |
+
"""
|
96 |
+
|
97 |
+
opts,par = self.parse_options(parameter_s,'ortq')
|
98 |
+
log_output = 'o' in opts
|
99 |
+
log_raw_input = 'r' in opts
|
100 |
+
timestamp = 't' in opts
|
101 |
+
quiet = 'q' in opts
|
102 |
+
|
103 |
+
logger = self.shell.logger
|
104 |
+
|
105 |
+
# if no args are given, the defaults set in the logger constructor by
|
106 |
+
# ipython remain valid
|
107 |
+
if par:
|
108 |
+
try:
|
109 |
+
logfname,logmode = par.split()
|
110 |
+
except:
|
111 |
+
logfname = par
|
112 |
+
logmode = 'backup'
|
113 |
+
else:
|
114 |
+
logfname = logger.logfname
|
115 |
+
logmode = logger.logmode
|
116 |
+
# put logfname into rc struct as if it had been called on the command
|
117 |
+
# line, so it ends up saved in the log header Save it in case we need
|
118 |
+
# to restore it...
|
119 |
+
old_logfile = self.shell.logfile
|
120 |
+
if logfname:
|
121 |
+
logfname = os.path.expanduser(logfname)
|
122 |
+
self.shell.logfile = logfname
|
123 |
+
|
124 |
+
loghead = u'# IPython log file\n\n'
|
125 |
+
try:
|
126 |
+
logger.logstart(logfname, loghead, logmode, log_output, timestamp,
|
127 |
+
log_raw_input)
|
128 |
+
except:
|
129 |
+
self.shell.logfile = old_logfile
|
130 |
+
warn("Couldn't start log: %s" % sys.exc_info()[1])
|
131 |
+
else:
|
132 |
+
# log input history up to this point, optionally interleaving
|
133 |
+
# output if requested
|
134 |
+
|
135 |
+
if timestamp:
|
136 |
+
# disable timestamping for the previous history, since we've
|
137 |
+
# lost those already (no time machine here).
|
138 |
+
logger.timestamp = False
|
139 |
+
|
140 |
+
if log_raw_input:
|
141 |
+
input_hist = self.shell.history_manager.input_hist_raw
|
142 |
+
else:
|
143 |
+
input_hist = self.shell.history_manager.input_hist_parsed
|
144 |
+
|
145 |
+
if log_output:
|
146 |
+
log_write = logger.log_write
|
147 |
+
output_hist = self.shell.history_manager.output_hist
|
148 |
+
for n in range(1,len(input_hist)-1):
|
149 |
+
log_write(input_hist[n].rstrip() + u'\n')
|
150 |
+
if n in output_hist:
|
151 |
+
log_write(repr(output_hist[n]),'output')
|
152 |
+
else:
|
153 |
+
logger.log_write(u'\n'.join(input_hist[1:]))
|
154 |
+
logger.log_write(u'\n')
|
155 |
+
if timestamp:
|
156 |
+
# re-enable timestamping
|
157 |
+
logger.timestamp = True
|
158 |
+
|
159 |
+
if not (self.quiet or quiet):
|
160 |
+
print ('Activating auto-logging. '
|
161 |
+
'Current session state plus future input saved.')
|
162 |
+
logger.logstate()
|
163 |
+
|
164 |
+
@line_magic
|
165 |
+
def logstop(self, parameter_s=''):
|
166 |
+
"""Fully stop logging and close log file.
|
167 |
+
|
168 |
+
In order to start logging again, a new %logstart call needs to be made,
|
169 |
+
possibly (though not necessarily) with a new filename, mode and other
|
170 |
+
options."""
|
171 |
+
self.shell.logger.logstop()
|
172 |
+
|
173 |
+
@line_magic
|
174 |
+
def logoff(self, parameter_s=''):
|
175 |
+
"""Temporarily stop logging.
|
176 |
+
|
177 |
+
You must have previously started logging."""
|
178 |
+
self.shell.logger.switch_log(0)
|
179 |
+
|
180 |
+
@line_magic
|
181 |
+
def logon(self, parameter_s=''):
|
182 |
+
"""Restart logging.
|
183 |
+
|
184 |
+
This function is for restarting logging which you've temporarily
|
185 |
+
stopped with %logoff. For starting logging for the first time, you
|
186 |
+
must use the %logstart function, which allows you to specify an
|
187 |
+
optional log filename."""
|
188 |
+
|
189 |
+
self.shell.logger.switch_log(1)
|
190 |
+
|
191 |
+
@line_magic
|
192 |
+
def logstate(self, parameter_s=''):
|
193 |
+
"""Print the status of the logging system."""
|
194 |
+
|
195 |
+
self.shell.logger.logstate()
|
temp_venv/lib/python3.13/site-packages/IPython/core/magics/namespace.py
ADDED
@@ -0,0 +1,711 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Implementation of namespace-related magic functions.
|
2 |
+
"""
|
3 |
+
#-----------------------------------------------------------------------------
|
4 |
+
# Copyright (c) 2012 The IPython Development Team.
|
5 |
+
#
|
6 |
+
# Distributed under the terms of the Modified BSD License.
|
7 |
+
#
|
8 |
+
# The full license is in the file COPYING.txt, distributed with this software.
|
9 |
+
#-----------------------------------------------------------------------------
|
10 |
+
|
11 |
+
#-----------------------------------------------------------------------------
|
12 |
+
# Imports
|
13 |
+
#-----------------------------------------------------------------------------
|
14 |
+
|
15 |
+
# Stdlib
|
16 |
+
import gc
|
17 |
+
import re
|
18 |
+
import sys
|
19 |
+
|
20 |
+
# Our own packages
|
21 |
+
from IPython.core import page
|
22 |
+
from IPython.core.error import StdinNotImplementedError, UsageError
|
23 |
+
from IPython.core.magic import Magics, magics_class, line_magic
|
24 |
+
from IPython.testing.skipdoctest import skip_doctest
|
25 |
+
from IPython.utils.encoding import DEFAULT_ENCODING
|
26 |
+
from IPython.utils.openpy import read_py_file
|
27 |
+
from IPython.utils.path import get_py_filename
|
28 |
+
|
29 |
+
#-----------------------------------------------------------------------------
|
30 |
+
# Magic implementation classes
|
31 |
+
#-----------------------------------------------------------------------------
|
32 |
+
|
33 |
+
@magics_class
|
34 |
+
class NamespaceMagics(Magics):
|
35 |
+
"""Magics to manage various aspects of the user's namespace.
|
36 |
+
|
37 |
+
These include listing variables, introspecting into them, etc.
|
38 |
+
"""
|
39 |
+
|
40 |
+
@line_magic
|
41 |
+
def pinfo(self, parameter_s='', namespaces=None):
|
42 |
+
"""Provide detailed information about an object.
|
43 |
+
|
44 |
+
'%pinfo object' is just a synonym for object? or ?object."""
|
45 |
+
|
46 |
+
# print('pinfo par: <%s>' % parameter_s) # dbg
|
47 |
+
# detail_level: 0 -> obj? , 1 -> obj??
|
48 |
+
detail_level = 0
|
49 |
+
# We need to detect if we got called as 'pinfo pinfo foo', which can
|
50 |
+
# happen if the user types 'pinfo foo?' at the cmd line.
|
51 |
+
pinfo,qmark1,oname,qmark2 = \
|
52 |
+
re.match(r'(pinfo )?(\?*)(.*?)(\??$)',parameter_s).groups()
|
53 |
+
if pinfo or qmark1 or qmark2:
|
54 |
+
detail_level = 1
|
55 |
+
if "*" in oname:
|
56 |
+
self.psearch(oname)
|
57 |
+
else:
|
58 |
+
self.shell._inspect('pinfo', oname, detail_level=detail_level,
|
59 |
+
namespaces=namespaces)
|
60 |
+
|
61 |
+
@line_magic
|
62 |
+
def pinfo2(self, parameter_s='', namespaces=None):
|
63 |
+
"""Provide extra detailed information about an object.
|
64 |
+
|
65 |
+
'%pinfo2 object' is just a synonym for object?? or ??object."""
|
66 |
+
self.shell._inspect('pinfo', parameter_s, detail_level=1,
|
67 |
+
namespaces=namespaces)
|
68 |
+
|
69 |
+
@skip_doctest
|
70 |
+
@line_magic
|
71 |
+
def pdef(self, parameter_s='', namespaces=None):
|
72 |
+
"""Print the call signature for any callable object.
|
73 |
+
|
74 |
+
If the object is a class, print the constructor information.
|
75 |
+
|
76 |
+
Examples
|
77 |
+
--------
|
78 |
+
::
|
79 |
+
|
80 |
+
In [3]: %pdef urllib.urlopen
|
81 |
+
urllib.urlopen(url, data=None, proxies=None)
|
82 |
+
"""
|
83 |
+
self.shell._inspect('pdef',parameter_s, namespaces)
|
84 |
+
|
85 |
+
@line_magic
|
86 |
+
def pdoc(self, parameter_s='', namespaces=None):
|
87 |
+
"""Print the docstring for an object.
|
88 |
+
|
89 |
+
If the given object is a class, it will print both the class and the
|
90 |
+
constructor docstrings."""
|
91 |
+
self.shell._inspect('pdoc',parameter_s, namespaces)
|
92 |
+
|
93 |
+
@line_magic
|
94 |
+
def psource(self, parameter_s='', namespaces=None):
|
95 |
+
"""Print (or run through pager) the source code for an object."""
|
96 |
+
if not parameter_s:
|
97 |
+
raise UsageError('Missing object name.')
|
98 |
+
self.shell._inspect('psource',parameter_s, namespaces)
|
99 |
+
|
100 |
+
@line_magic
|
101 |
+
def pfile(self, parameter_s='', namespaces=None):
|
102 |
+
"""Print (or run through pager) the file where an object is defined.
|
103 |
+
|
104 |
+
The file opens at the line where the object definition begins. IPython
|
105 |
+
will honor the environment variable PAGER if set, and otherwise will
|
106 |
+
do its best to print the file in a convenient form.
|
107 |
+
|
108 |
+
If the given argument is not an object currently defined, IPython will
|
109 |
+
try to interpret it as a filename (automatically adding a .py extension
|
110 |
+
if needed). You can thus use %pfile as a syntax highlighting code
|
111 |
+
viewer."""
|
112 |
+
|
113 |
+
# first interpret argument as an object name
|
114 |
+
out = self.shell._inspect('pfile',parameter_s, namespaces)
|
115 |
+
# if not, try the input as a filename
|
116 |
+
if out == 'not found':
|
117 |
+
try:
|
118 |
+
filename = get_py_filename(parameter_s)
|
119 |
+
except IOError as msg:
|
120 |
+
print(msg)
|
121 |
+
return
|
122 |
+
page.page(self.shell.pycolorize(read_py_file(filename, skip_encoding_cookie=False)))
|
123 |
+
|
124 |
+
@line_magic
|
125 |
+
def psearch(self, parameter_s=''):
|
126 |
+
"""Search for object in namespaces by wildcard.
|
127 |
+
|
128 |
+
%psearch [options] PATTERN [OBJECT TYPE]
|
129 |
+
|
130 |
+
Note: ? can be used as a synonym for %psearch, at the beginning or at
|
131 |
+
the end: both a*? and ?a* are equivalent to '%psearch a*'. Still, the
|
132 |
+
rest of the command line must be unchanged (options come first), so
|
133 |
+
for example the following forms are equivalent
|
134 |
+
|
135 |
+
%psearch -i a* function
|
136 |
+
-i a* function?
|
137 |
+
?-i a* function
|
138 |
+
|
139 |
+
Arguments:
|
140 |
+
|
141 |
+
PATTERN
|
142 |
+
|
143 |
+
where PATTERN is a string containing * as a wildcard similar to its
|
144 |
+
use in a shell. The pattern is matched in all namespaces on the
|
145 |
+
search path. By default objects starting with a single _ are not
|
146 |
+
matched, many IPython generated objects have a single
|
147 |
+
underscore. The default is case insensitive matching. Matching is
|
148 |
+
also done on the attributes of objects and not only on the objects
|
149 |
+
in a module.
|
150 |
+
|
151 |
+
[OBJECT TYPE]
|
152 |
+
|
153 |
+
Is the name of a python type from the types module. The name is
|
154 |
+
given in lowercase without the ending type, ex. StringType is
|
155 |
+
written string. By adding a type here only objects matching the
|
156 |
+
given type are matched. Using all here makes the pattern match all
|
157 |
+
types (this is the default).
|
158 |
+
|
159 |
+
Options:
|
160 |
+
|
161 |
+
-a: makes the pattern match even objects whose names start with a
|
162 |
+
single underscore. These names are normally omitted from the
|
163 |
+
search.
|
164 |
+
|
165 |
+
-i/-c: make the pattern case insensitive/sensitive. If neither of
|
166 |
+
these options are given, the default is read from your configuration
|
167 |
+
file, with the option ``InteractiveShell.wildcards_case_sensitive``.
|
168 |
+
If this option is not specified in your configuration file, IPython's
|
169 |
+
internal default is to do a case sensitive search.
|
170 |
+
|
171 |
+
-e/-s NAMESPACE: exclude/search a given namespace. The pattern you
|
172 |
+
specify can be searched in any of the following namespaces:
|
173 |
+
'builtin', 'user', 'user_global','internal', 'alias', where
|
174 |
+
'builtin' and 'user' are the search defaults. Note that you should
|
175 |
+
not use quotes when specifying namespaces.
|
176 |
+
|
177 |
+
-l: List all available object types for object matching. This function
|
178 |
+
can be used without arguments.
|
179 |
+
|
180 |
+
'Builtin' contains the python module builtin, 'user' contains all
|
181 |
+
user data, 'alias' only contain the shell aliases and no python
|
182 |
+
objects, 'internal' contains objects used by IPython. The
|
183 |
+
'user_global' namespace is only used by embedded IPython instances,
|
184 |
+
and it contains module-level globals. You can add namespaces to the
|
185 |
+
search with -s or exclude them with -e (these options can be given
|
186 |
+
more than once).
|
187 |
+
|
188 |
+
Examples
|
189 |
+
--------
|
190 |
+
::
|
191 |
+
|
192 |
+
%psearch a* -> objects beginning with an a
|
193 |
+
%psearch -e builtin a* -> objects NOT in the builtin space starting in a
|
194 |
+
%psearch a* function -> all functions beginning with an a
|
195 |
+
%psearch re.e* -> objects beginning with an e in module re
|
196 |
+
%psearch r*.e* -> objects that start with e in modules starting in r
|
197 |
+
%psearch r*.* string -> all strings in modules beginning with r
|
198 |
+
|
199 |
+
Case sensitive search::
|
200 |
+
|
201 |
+
%psearch -c a* list all object beginning with lower case a
|
202 |
+
|
203 |
+
Show objects beginning with a single _::
|
204 |
+
|
205 |
+
%psearch -a _* list objects beginning with a single underscore
|
206 |
+
|
207 |
+
List available objects::
|
208 |
+
|
209 |
+
%psearch -l list all available object types
|
210 |
+
"""
|
211 |
+
# default namespaces to be searched
|
212 |
+
def_search = ['user_local', 'user_global', 'builtin']
|
213 |
+
|
214 |
+
# Process options/args
|
215 |
+
opts,args = self.parse_options(parameter_s,'cias:e:l',list_all=True)
|
216 |
+
opt = opts.get
|
217 |
+
shell = self.shell
|
218 |
+
psearch = shell.inspector.psearch
|
219 |
+
|
220 |
+
# select list object types
|
221 |
+
list_types = False
|
222 |
+
if 'l' in opts:
|
223 |
+
list_types = True
|
224 |
+
|
225 |
+
# select case options
|
226 |
+
if 'i' in opts:
|
227 |
+
ignore_case = True
|
228 |
+
elif 'c' in opts:
|
229 |
+
ignore_case = False
|
230 |
+
else:
|
231 |
+
ignore_case = not shell.wildcards_case_sensitive
|
232 |
+
|
233 |
+
# Build list of namespaces to search from user options
|
234 |
+
def_search.extend(opt('s',[]))
|
235 |
+
ns_exclude = ns_exclude=opt('e',[])
|
236 |
+
ns_search = [nm for nm in def_search if nm not in ns_exclude]
|
237 |
+
|
238 |
+
# Call the actual search
|
239 |
+
try:
|
240 |
+
psearch(args,shell.ns_table,ns_search,
|
241 |
+
show_all=opt('a'),ignore_case=ignore_case, list_types=list_types)
|
242 |
+
except:
|
243 |
+
shell.showtraceback()
|
244 |
+
|
245 |
+
@skip_doctest
|
246 |
+
@line_magic
|
247 |
+
def who_ls(self, parameter_s=''):
|
248 |
+
"""Return a sorted list of all interactive variables.
|
249 |
+
|
250 |
+
If arguments are given, only variables of types matching these
|
251 |
+
arguments are returned.
|
252 |
+
|
253 |
+
Examples
|
254 |
+
--------
|
255 |
+
Define two variables and list them with who_ls::
|
256 |
+
|
257 |
+
In [1]: alpha = 123
|
258 |
+
|
259 |
+
In [2]: beta = 'test'
|
260 |
+
|
261 |
+
In [3]: %who_ls
|
262 |
+
Out[3]: ['alpha', 'beta']
|
263 |
+
|
264 |
+
In [4]: %who_ls int
|
265 |
+
Out[4]: ['alpha']
|
266 |
+
|
267 |
+
In [5]: %who_ls str
|
268 |
+
Out[5]: ['beta']
|
269 |
+
"""
|
270 |
+
|
271 |
+
user_ns = self.shell.user_ns
|
272 |
+
user_ns_hidden = self.shell.user_ns_hidden
|
273 |
+
nonmatching = object() # This can never be in user_ns
|
274 |
+
out = [ i for i in user_ns
|
275 |
+
if not i.startswith('_') \
|
276 |
+
and (user_ns[i] is not user_ns_hidden.get(i, nonmatching)) ]
|
277 |
+
|
278 |
+
typelist = parameter_s.split()
|
279 |
+
if typelist:
|
280 |
+
typeset = set(typelist)
|
281 |
+
out = [i for i in out if type(user_ns[i]).__name__ in typeset]
|
282 |
+
|
283 |
+
out.sort()
|
284 |
+
return out
|
285 |
+
|
286 |
+
@skip_doctest
|
287 |
+
@line_magic
|
288 |
+
def who(self, parameter_s=''):
|
289 |
+
"""Print all interactive variables, with some minimal formatting.
|
290 |
+
|
291 |
+
If any arguments are given, only variables whose type matches one of
|
292 |
+
these are printed. For example::
|
293 |
+
|
294 |
+
%who function str
|
295 |
+
|
296 |
+
will only list functions and strings, excluding all other types of
|
297 |
+
variables. To find the proper type names, simply use type(var) at a
|
298 |
+
command line to see how python prints type names. For example:
|
299 |
+
|
300 |
+
::
|
301 |
+
|
302 |
+
In [1]: type('hello')\\
|
303 |
+
Out[1]: <type 'str'>
|
304 |
+
|
305 |
+
indicates that the type name for strings is 'str'.
|
306 |
+
|
307 |
+
``%who`` always excludes executed names loaded through your configuration
|
308 |
+
file and things which are internal to IPython.
|
309 |
+
|
310 |
+
This is deliberate, as typically you may load many modules and the
|
311 |
+
purpose of %who is to show you only what you've manually defined.
|
312 |
+
|
313 |
+
Examples
|
314 |
+
--------
|
315 |
+
|
316 |
+
Define two variables and list them with who::
|
317 |
+
|
318 |
+
In [1]: alpha = 123
|
319 |
+
|
320 |
+
In [2]: beta = 'test'
|
321 |
+
|
322 |
+
In [3]: %who
|
323 |
+
alpha beta
|
324 |
+
|
325 |
+
In [4]: %who int
|
326 |
+
alpha
|
327 |
+
|
328 |
+
In [5]: %who str
|
329 |
+
beta
|
330 |
+
"""
|
331 |
+
|
332 |
+
varlist = self.who_ls(parameter_s)
|
333 |
+
if not varlist:
|
334 |
+
if parameter_s:
|
335 |
+
print('No variables match your requested type.')
|
336 |
+
else:
|
337 |
+
print('Interactive namespace is empty.')
|
338 |
+
return
|
339 |
+
|
340 |
+
# if we have variables, move on...
|
341 |
+
count = 0
|
342 |
+
for i in varlist:
|
343 |
+
print(i+'\t', end=' ')
|
344 |
+
count += 1
|
345 |
+
if count > 8:
|
346 |
+
count = 0
|
347 |
+
print()
|
348 |
+
print()
|
349 |
+
|
350 |
+
@skip_doctest
|
351 |
+
@line_magic
|
352 |
+
def whos(self, parameter_s=''):
|
353 |
+
"""Like %who, but gives some extra information about each variable.
|
354 |
+
|
355 |
+
The same type filtering of %who can be applied here.
|
356 |
+
|
357 |
+
For all variables, the type is printed. Additionally it prints:
|
358 |
+
|
359 |
+
- For {},[],(): their length.
|
360 |
+
|
361 |
+
- For numpy arrays, a summary with shape, number of
|
362 |
+
elements, typecode and size in memory.
|
363 |
+
|
364 |
+
- Everything else: a string representation, snipping their middle if
|
365 |
+
too long.
|
366 |
+
|
367 |
+
Examples
|
368 |
+
--------
|
369 |
+
Define two variables and list them with whos::
|
370 |
+
|
371 |
+
In [1]: alpha = 123
|
372 |
+
|
373 |
+
In [2]: beta = 'test'
|
374 |
+
|
375 |
+
In [3]: %whos
|
376 |
+
Variable Type Data/Info
|
377 |
+
--------------------------------
|
378 |
+
alpha int 123
|
379 |
+
beta str test
|
380 |
+
"""
|
381 |
+
|
382 |
+
varnames = self.who_ls(parameter_s)
|
383 |
+
if not varnames:
|
384 |
+
if parameter_s:
|
385 |
+
print('No variables match your requested type.')
|
386 |
+
else:
|
387 |
+
print('Interactive namespace is empty.')
|
388 |
+
return
|
389 |
+
|
390 |
+
# if we have variables, move on...
|
391 |
+
|
392 |
+
# for these types, show len() instead of data:
|
393 |
+
seq_types = ['dict', 'list', 'tuple']
|
394 |
+
|
395 |
+
# for numpy arrays, display summary info
|
396 |
+
ndarray_type = None
|
397 |
+
if 'numpy' in sys.modules:
|
398 |
+
try:
|
399 |
+
from numpy import ndarray
|
400 |
+
except ImportError:
|
401 |
+
pass
|
402 |
+
else:
|
403 |
+
ndarray_type = ndarray.__name__
|
404 |
+
|
405 |
+
# Find all variable names and types so we can figure out column sizes
|
406 |
+
|
407 |
+
# some types are well known and can be shorter
|
408 |
+
abbrevs = {'IPython.core.macro.Macro' : 'Macro'}
|
409 |
+
def type_name(v):
|
410 |
+
tn = type(v).__name__
|
411 |
+
return abbrevs.get(tn,tn)
|
412 |
+
|
413 |
+
varlist = [self.shell.user_ns[n] for n in varnames]
|
414 |
+
|
415 |
+
typelist = []
|
416 |
+
for vv in varlist:
|
417 |
+
tt = type_name(vv)
|
418 |
+
|
419 |
+
if tt=='instance':
|
420 |
+
typelist.append( abbrevs.get(str(vv.__class__),
|
421 |
+
str(vv.__class__)))
|
422 |
+
else:
|
423 |
+
typelist.append(tt)
|
424 |
+
|
425 |
+
# column labels and # of spaces as separator
|
426 |
+
varlabel = 'Variable'
|
427 |
+
typelabel = 'Type'
|
428 |
+
datalabel = 'Data/Info'
|
429 |
+
colsep = 3
|
430 |
+
# variable format strings
|
431 |
+
vformat = "{0:<{varwidth}}{1:<{typewidth}}"
|
432 |
+
aformat = "%s: %s elems, type `%s`, %s bytes"
|
433 |
+
# find the size of the columns to format the output nicely
|
434 |
+
varwidth = max(max(map(len,varnames)), len(varlabel)) + colsep
|
435 |
+
typewidth = max(max(map(len,typelist)), len(typelabel)) + colsep
|
436 |
+
# table header
|
437 |
+
print(varlabel.ljust(varwidth) + typelabel.ljust(typewidth) + \
|
438 |
+
' '+datalabel+'\n' + '-'*(varwidth+typewidth+len(datalabel)+1))
|
439 |
+
# and the table itself
|
440 |
+
kb = 1024
|
441 |
+
Mb = 1048576 # kb**2
|
442 |
+
for vname,var,vtype in zip(varnames,varlist,typelist):
|
443 |
+
print(vformat.format(vname, vtype, varwidth=varwidth, typewidth=typewidth), end=' ')
|
444 |
+
if vtype in seq_types:
|
445 |
+
print("n="+str(len(var)))
|
446 |
+
elif vtype == ndarray_type:
|
447 |
+
vshape = str(var.shape).replace(',','').replace(' ','x')[1:-1]
|
448 |
+
if vtype==ndarray_type:
|
449 |
+
# numpy
|
450 |
+
vsize = var.size
|
451 |
+
vbytes = vsize*var.itemsize
|
452 |
+
vdtype = var.dtype
|
453 |
+
|
454 |
+
if vbytes < 100000:
|
455 |
+
print(aformat % (vshape, vsize, vdtype, vbytes))
|
456 |
+
else:
|
457 |
+
print(aformat % (vshape, vsize, vdtype, vbytes), end=' ')
|
458 |
+
if vbytes < Mb:
|
459 |
+
print('(%s kb)' % (vbytes/kb,))
|
460 |
+
else:
|
461 |
+
print('(%s Mb)' % (vbytes/Mb,))
|
462 |
+
else:
|
463 |
+
try:
|
464 |
+
vstr = str(var)
|
465 |
+
except UnicodeEncodeError:
|
466 |
+
vstr = var.encode(DEFAULT_ENCODING,
|
467 |
+
'backslashreplace')
|
468 |
+
except:
|
469 |
+
vstr = "<object with id %d (str() failed)>" % id(var)
|
470 |
+
vstr = vstr.replace('\n', '\\n')
|
471 |
+
if len(vstr) < 50:
|
472 |
+
print(vstr)
|
473 |
+
else:
|
474 |
+
print(vstr[:25] + "<...>" + vstr[-25:])
|
475 |
+
|
476 |
+
@line_magic
|
477 |
+
def reset(self, parameter_s=''):
|
478 |
+
"""Resets the namespace by removing all names defined by the user, if
|
479 |
+
called without arguments, or by removing some types of objects, such
|
480 |
+
as everything currently in IPython's In[] and Out[] containers (see
|
481 |
+
the parameters for details).
|
482 |
+
|
483 |
+
Parameters
|
484 |
+
----------
|
485 |
+
-f
|
486 |
+
force reset without asking for confirmation.
|
487 |
+
-s
|
488 |
+
'Soft' reset: Only clears your namespace, leaving history intact.
|
489 |
+
References to objects may be kept. By default (without this option),
|
490 |
+
we do a 'hard' reset, giving you a new session and removing all
|
491 |
+
references to objects from the current session.
|
492 |
+
--aggressive
|
493 |
+
Try to aggressively remove modules from sys.modules ; this
|
494 |
+
may allow you to reimport Python modules that have been updated and
|
495 |
+
pick up changes, but can have unintended consequences.
|
496 |
+
|
497 |
+
in
|
498 |
+
reset input history
|
499 |
+
out
|
500 |
+
reset output history
|
501 |
+
dhist
|
502 |
+
reset directory history
|
503 |
+
array
|
504 |
+
reset only variables that are NumPy arrays
|
505 |
+
|
506 |
+
See Also
|
507 |
+
--------
|
508 |
+
reset_selective : invoked as ``%reset_selective``
|
509 |
+
|
510 |
+
Examples
|
511 |
+
--------
|
512 |
+
::
|
513 |
+
|
514 |
+
In [6]: a = 1
|
515 |
+
|
516 |
+
In [7]: a
|
517 |
+
Out[7]: 1
|
518 |
+
|
519 |
+
In [8]: 'a' in get_ipython().user_ns
|
520 |
+
Out[8]: True
|
521 |
+
|
522 |
+
In [9]: %reset -f
|
523 |
+
|
524 |
+
In [1]: 'a' in get_ipython().user_ns
|
525 |
+
Out[1]: False
|
526 |
+
|
527 |
+
In [2]: %reset -f in
|
528 |
+
Flushing input history
|
529 |
+
|
530 |
+
In [3]: %reset -f dhist in
|
531 |
+
Flushing directory history
|
532 |
+
Flushing input history
|
533 |
+
|
534 |
+
Notes
|
535 |
+
-----
|
536 |
+
Calling this magic from clients that do not implement standard input,
|
537 |
+
such as the ipython notebook interface, will reset the namespace
|
538 |
+
without confirmation.
|
539 |
+
"""
|
540 |
+
opts, args = self.parse_options(parameter_s, "sf", "aggressive", mode="list")
|
541 |
+
if "f" in opts:
|
542 |
+
ans = True
|
543 |
+
else:
|
544 |
+
try:
|
545 |
+
ans = self.shell.ask_yes_no(
|
546 |
+
"Once deleted, variables cannot be recovered. Proceed (y/[n])?",
|
547 |
+
default='n')
|
548 |
+
except StdinNotImplementedError:
|
549 |
+
ans = True
|
550 |
+
if not ans:
|
551 |
+
print('Nothing done.')
|
552 |
+
return
|
553 |
+
|
554 |
+
if 's' in opts: # Soft reset
|
555 |
+
user_ns = self.shell.user_ns
|
556 |
+
for i in self.who_ls():
|
557 |
+
del(user_ns[i])
|
558 |
+
elif len(args) == 0: # Hard reset
|
559 |
+
self.shell.reset(new_session=False, aggressive=("aggressive" in opts))
|
560 |
+
|
561 |
+
# reset in/out/dhist/array: previously extensinions/clearcmd.py
|
562 |
+
ip = self.shell
|
563 |
+
user_ns = self.shell.user_ns # local lookup, heavily used
|
564 |
+
|
565 |
+
for target in args:
|
566 |
+
target = target.lower() # make matches case insensitive
|
567 |
+
if target == 'out':
|
568 |
+
print("Flushing output cache (%d entries)" % len(user_ns['_oh']))
|
569 |
+
self.shell.displayhook.flush()
|
570 |
+
|
571 |
+
elif target == 'in':
|
572 |
+
print("Flushing input history")
|
573 |
+
pc = self.shell.displayhook.prompt_count + 1
|
574 |
+
for n in range(1, pc):
|
575 |
+
key = '_i'+repr(n)
|
576 |
+
user_ns.pop(key,None)
|
577 |
+
user_ns.update(dict(_i=u'',_ii=u'',_iii=u''))
|
578 |
+
hm = ip.history_manager
|
579 |
+
# don't delete these, as %save and %macro depending on the
|
580 |
+
# length of these lists to be preserved
|
581 |
+
hm.input_hist_parsed[:] = [''] * pc
|
582 |
+
hm.input_hist_raw[:] = [''] * pc
|
583 |
+
# hm has internal machinery for _i,_ii,_iii, clear it out
|
584 |
+
hm._i = hm._ii = hm._iii = hm._i00 = u''
|
585 |
+
|
586 |
+
elif target == 'array':
|
587 |
+
# Support cleaning up numpy arrays
|
588 |
+
try:
|
589 |
+
from numpy import ndarray
|
590 |
+
# This must be done with items and not iteritems because
|
591 |
+
# we're going to modify the dict in-place.
|
592 |
+
for x,val in list(user_ns.items()):
|
593 |
+
if isinstance(val,ndarray):
|
594 |
+
del user_ns[x]
|
595 |
+
except ImportError:
|
596 |
+
print("reset array only works if Numpy is available.")
|
597 |
+
|
598 |
+
elif target == 'dhist':
|
599 |
+
print("Flushing directory history")
|
600 |
+
del user_ns['_dh'][:]
|
601 |
+
|
602 |
+
else:
|
603 |
+
print("Don't know how to reset ", end=' ')
|
604 |
+
print(target + ", please run `%reset?` for details")
|
605 |
+
|
606 |
+
gc.collect()
|
607 |
+
|
608 |
+
@line_magic
|
609 |
+
def reset_selective(self, parameter_s=''):
|
610 |
+
"""Resets the namespace by removing names defined by the user.
|
611 |
+
|
612 |
+
Input/Output history are left around in case you need them.
|
613 |
+
|
614 |
+
%reset_selective [-f] regex
|
615 |
+
|
616 |
+
No action is taken if regex is not included
|
617 |
+
|
618 |
+
Options
|
619 |
+
-f : force reset without asking for confirmation.
|
620 |
+
|
621 |
+
See Also
|
622 |
+
--------
|
623 |
+
reset : invoked as ``%reset``
|
624 |
+
|
625 |
+
Examples
|
626 |
+
--------
|
627 |
+
We first fully reset the namespace so your output looks identical to
|
628 |
+
this example for pedagogical reasons; in practice you do not need a
|
629 |
+
full reset::
|
630 |
+
|
631 |
+
In [1]: %reset -f
|
632 |
+
|
633 |
+
Now, with a clean namespace we can make a few variables and use
|
634 |
+
``%reset_selective`` to only delete names that match our regexp::
|
635 |
+
|
636 |
+
In [2]: a=1; b=2; c=3; b1m=4; b2m=5; b3m=6; b4m=7; b2s=8
|
637 |
+
|
638 |
+
In [3]: who_ls
|
639 |
+
Out[3]: ['a', 'b', 'b1m', 'b2m', 'b2s', 'b3m', 'b4m', 'c']
|
640 |
+
|
641 |
+
In [4]: %reset_selective -f b[2-3]m
|
642 |
+
|
643 |
+
In [5]: who_ls
|
644 |
+
Out[5]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c']
|
645 |
+
|
646 |
+
In [6]: %reset_selective -f d
|
647 |
+
|
648 |
+
In [7]: who_ls
|
649 |
+
Out[7]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c']
|
650 |
+
|
651 |
+
In [8]: %reset_selective -f c
|
652 |
+
|
653 |
+
In [9]: who_ls
|
654 |
+
Out[9]: ['a', 'b', 'b1m', 'b2s', 'b4m']
|
655 |
+
|
656 |
+
In [10]: %reset_selective -f b
|
657 |
+
|
658 |
+
In [11]: who_ls
|
659 |
+
Out[11]: ['a']
|
660 |
+
|
661 |
+
Notes
|
662 |
+
-----
|
663 |
+
Calling this magic from clients that do not implement standard input,
|
664 |
+
such as the ipython notebook interface, will reset the namespace
|
665 |
+
without confirmation.
|
666 |
+
"""
|
667 |
+
|
668 |
+
opts, regex = self.parse_options(parameter_s,'f')
|
669 |
+
|
670 |
+
if 'f' in opts:
|
671 |
+
ans = True
|
672 |
+
else:
|
673 |
+
try:
|
674 |
+
ans = self.shell.ask_yes_no(
|
675 |
+
"Once deleted, variables cannot be recovered. Proceed (y/[n])? ",
|
676 |
+
default='n')
|
677 |
+
except StdinNotImplementedError:
|
678 |
+
ans = True
|
679 |
+
if not ans:
|
680 |
+
print('Nothing done.')
|
681 |
+
return
|
682 |
+
user_ns = self.shell.user_ns
|
683 |
+
if not regex:
|
684 |
+
print('No regex pattern specified. Nothing done.')
|
685 |
+
return
|
686 |
+
else:
|
687 |
+
try:
|
688 |
+
m = re.compile(regex)
|
689 |
+
except TypeError as e:
|
690 |
+
raise TypeError('regex must be a string or compiled pattern') from e
|
691 |
+
for i in self.who_ls():
|
692 |
+
if m.search(i):
|
693 |
+
del(user_ns[i])
|
694 |
+
|
695 |
+
@line_magic
|
696 |
+
def xdel(self, parameter_s=''):
|
697 |
+
"""Delete a variable, trying to clear it from anywhere that
|
698 |
+
IPython's machinery has references to it. By default, this uses
|
699 |
+
the identity of the named object in the user namespace to remove
|
700 |
+
references held under other names. The object is also removed
|
701 |
+
from the output history.
|
702 |
+
|
703 |
+
Options
|
704 |
+
-n : Delete the specified name from all namespaces, without
|
705 |
+
checking their identity.
|
706 |
+
"""
|
707 |
+
opts, varname = self.parse_options(parameter_s,'n')
|
708 |
+
try:
|
709 |
+
self.shell.del_var(varname, ('n' in opts))
|
710 |
+
except (NameError, ValueError) as e:
|
711 |
+
print(type(e).__name__ +": "+ str(e))
|
temp_venv/lib/python3.13/site-packages/IPython/core/magics/osm.py
ADDED
@@ -0,0 +1,855 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Implementation of magic functions for interaction with the OS.
|
2 |
+
|
3 |
+
Note: this module is named 'osm' instead of 'os' to avoid a collision with the
|
4 |
+
builtin.
|
5 |
+
"""
|
6 |
+
# Copyright (c) IPython Development Team.
|
7 |
+
# Distributed under the terms of the Modified BSD License.
|
8 |
+
|
9 |
+
import io
|
10 |
+
import os
|
11 |
+
import pathlib
|
12 |
+
import re
|
13 |
+
import sys
|
14 |
+
from pprint import pformat
|
15 |
+
|
16 |
+
from IPython.core import magic_arguments
|
17 |
+
from IPython.core import oinspect
|
18 |
+
from IPython.core import page
|
19 |
+
from IPython.core.alias import AliasError, Alias
|
20 |
+
from IPython.core.error import UsageError
|
21 |
+
from IPython.core.magic import (
|
22 |
+
Magics, compress_dhist, magics_class, line_magic, cell_magic, line_cell_magic
|
23 |
+
)
|
24 |
+
from IPython.testing.skipdoctest import skip_doctest
|
25 |
+
from IPython.utils.openpy import source_to_unicode
|
26 |
+
from IPython.utils.process import abbrev_cwd
|
27 |
+
from IPython.utils.terminal import set_term_title
|
28 |
+
from traitlets import Bool
|
29 |
+
from warnings import warn
|
30 |
+
|
31 |
+
|
32 |
+
@magics_class
|
33 |
+
class OSMagics(Magics):
|
34 |
+
"""Magics to interact with the underlying OS (shell-type functionality).
|
35 |
+
"""
|
36 |
+
|
37 |
+
cd_force_quiet = Bool(False,
|
38 |
+
help="Force %cd magic to be quiet even if -q is not passed."
|
39 |
+
).tag(config=True)
|
40 |
+
|
41 |
+
def __init__(self, shell=None, **kwargs):
|
42 |
+
|
43 |
+
# Now define isexec in a cross platform manner.
|
44 |
+
self.is_posix = False
|
45 |
+
self.execre = None
|
46 |
+
if os.name == 'posix':
|
47 |
+
self.is_posix = True
|
48 |
+
else:
|
49 |
+
try:
|
50 |
+
winext = os.environ['pathext'].replace(';','|').replace('.','')
|
51 |
+
except KeyError:
|
52 |
+
winext = 'exe|com|bat|py'
|
53 |
+
try:
|
54 |
+
self.execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE)
|
55 |
+
except re.error:
|
56 |
+
warn("Seems like your pathext environmental "
|
57 |
+
"variable is malformed. Please check it to "
|
58 |
+
"enable a proper handle of file extensions "
|
59 |
+
"managed for your system")
|
60 |
+
winext = 'exe|com|bat|py'
|
61 |
+
self.execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE)
|
62 |
+
|
63 |
+
# call up the chain
|
64 |
+
super().__init__(shell=shell, **kwargs)
|
65 |
+
|
66 |
+
|
67 |
+
def _isexec_POSIX(self, file):
|
68 |
+
"""
|
69 |
+
Test for executable on a POSIX system
|
70 |
+
"""
|
71 |
+
if os.access(file.path, os.X_OK):
|
72 |
+
# will fail on maxOS if access is not X_OK
|
73 |
+
return file.is_file()
|
74 |
+
return False
|
75 |
+
|
76 |
+
|
77 |
+
|
78 |
+
def _isexec_WIN(self, file):
|
79 |
+
"""
|
80 |
+
Test for executable file on non POSIX system
|
81 |
+
"""
|
82 |
+
return file.is_file() and self.execre.match(file.name) is not None
|
83 |
+
|
84 |
+
def isexec(self, file):
|
85 |
+
"""
|
86 |
+
Test for executable file on non POSIX system
|
87 |
+
"""
|
88 |
+
if self.is_posix:
|
89 |
+
return self._isexec_POSIX(file)
|
90 |
+
else:
|
91 |
+
return self._isexec_WIN(file)
|
92 |
+
|
93 |
+
|
94 |
+
@skip_doctest
|
95 |
+
@line_magic
|
96 |
+
def alias(self, parameter_s=''):
|
97 |
+
"""Define an alias for a system command.
|
98 |
+
|
99 |
+
'%alias alias_name cmd' defines 'alias_name' as an alias for 'cmd'
|
100 |
+
|
101 |
+
Then, typing 'alias_name params' will execute the system command 'cmd
|
102 |
+
params' (from your underlying operating system).
|
103 |
+
|
104 |
+
Aliases have lower precedence than magic functions and Python normal
|
105 |
+
variables, so if 'foo' is both a Python variable and an alias, the
|
106 |
+
alias can not be executed until 'del foo' removes the Python variable.
|
107 |
+
|
108 |
+
You can use the %l specifier in an alias definition to represent the
|
109 |
+
whole line when the alias is called. For example::
|
110 |
+
|
111 |
+
In [2]: alias bracket echo "Input in brackets: <%l>"
|
112 |
+
In [3]: bracket hello world
|
113 |
+
Input in brackets: <hello world>
|
114 |
+
|
115 |
+
You can also define aliases with parameters using %s specifiers (one
|
116 |
+
per parameter)::
|
117 |
+
|
118 |
+
In [1]: alias parts echo first %s second %s
|
119 |
+
In [2]: %parts A B
|
120 |
+
first A second B
|
121 |
+
In [3]: %parts A
|
122 |
+
Incorrect number of arguments: 2 expected.
|
123 |
+
parts is an alias to: 'echo first %s second %s'
|
124 |
+
|
125 |
+
Note that %l and %s are mutually exclusive. You can only use one or
|
126 |
+
the other in your aliases.
|
127 |
+
|
128 |
+
Aliases expand Python variables just like system calls using ! or !!
|
129 |
+
do: all expressions prefixed with '$' get expanded. For details of
|
130 |
+
the semantic rules, see PEP-215:
|
131 |
+
https://peps.python.org/pep-0215/. This is the library used by
|
132 |
+
IPython for variable expansion. If you want to access a true shell
|
133 |
+
variable, an extra $ is necessary to prevent its expansion by
|
134 |
+
IPython::
|
135 |
+
|
136 |
+
In [6]: alias show echo
|
137 |
+
In [7]: PATH='A Python string'
|
138 |
+
In [8]: show $PATH
|
139 |
+
A Python string
|
140 |
+
In [9]: show $$PATH
|
141 |
+
/usr/local/lf9560/bin:/usr/local/intel/compiler70/ia32/bin:...
|
142 |
+
|
143 |
+
You can use the alias facility to access all of $PATH. See the %rehashx
|
144 |
+
function, which automatically creates aliases for the contents of your
|
145 |
+
$PATH.
|
146 |
+
|
147 |
+
If called with no parameters, %alias prints the current alias table
|
148 |
+
for your system. For posix systems, the default aliases are 'cat',
|
149 |
+
'cp', 'mv', 'rm', 'rmdir', and 'mkdir', and other platform-specific
|
150 |
+
aliases are added. For windows-based systems, the default aliases are
|
151 |
+
'copy', 'ddir', 'echo', 'ls', 'ldir', 'mkdir', 'ren', and 'rmdir'.
|
152 |
+
|
153 |
+
You can see the definition of alias by adding a question mark in the
|
154 |
+
end::
|
155 |
+
|
156 |
+
In [1]: cat?
|
157 |
+
Repr: <alias cat for 'cat'>"""
|
158 |
+
|
159 |
+
par = parameter_s.strip()
|
160 |
+
if not par:
|
161 |
+
aliases = sorted(self.shell.alias_manager.aliases)
|
162 |
+
# stored = self.shell.db.get('stored_aliases', {} )
|
163 |
+
# for k, v in stored:
|
164 |
+
# atab.append(k, v[0])
|
165 |
+
|
166 |
+
print("Total number of aliases:", len(aliases))
|
167 |
+
sys.stdout.flush()
|
168 |
+
return aliases
|
169 |
+
|
170 |
+
# Now try to define a new one
|
171 |
+
try:
|
172 |
+
alias,cmd = par.split(None, 1)
|
173 |
+
except TypeError:
|
174 |
+
print(oinspect.getdoc(self.alias))
|
175 |
+
return
|
176 |
+
|
177 |
+
try:
|
178 |
+
self.shell.alias_manager.define_alias(alias, cmd)
|
179 |
+
except AliasError as e:
|
180 |
+
print(e)
|
181 |
+
# end magic_alias
|
182 |
+
|
183 |
+
@line_magic
|
184 |
+
def unalias(self, parameter_s=''):
|
185 |
+
"""Remove an alias"""
|
186 |
+
|
187 |
+
aname = parameter_s.strip()
|
188 |
+
try:
|
189 |
+
self.shell.alias_manager.undefine_alias(aname)
|
190 |
+
except ValueError as e:
|
191 |
+
print(e)
|
192 |
+
return
|
193 |
+
|
194 |
+
stored = self.shell.db.get('stored_aliases', {} )
|
195 |
+
if aname in stored:
|
196 |
+
print("Removing %stored alias",aname)
|
197 |
+
del stored[aname]
|
198 |
+
self.shell.db['stored_aliases'] = stored
|
199 |
+
|
200 |
+
@line_magic
|
201 |
+
def rehashx(self, parameter_s=''):
|
202 |
+
"""Update the alias table with all executable files in $PATH.
|
203 |
+
|
204 |
+
rehashx explicitly checks that every entry in $PATH is a file
|
205 |
+
with execute access (os.X_OK).
|
206 |
+
|
207 |
+
Under Windows, it checks executability as a match against a
|
208 |
+
'|'-separated string of extensions, stored in the IPython config
|
209 |
+
variable win_exec_ext. This defaults to 'exe|com|bat'.
|
210 |
+
|
211 |
+
This function also resets the root module cache of module completer,
|
212 |
+
used on slow filesystems.
|
213 |
+
"""
|
214 |
+
from IPython.core.alias import InvalidAliasError
|
215 |
+
|
216 |
+
# for the benefit of module completer in ipy_completers.py
|
217 |
+
del self.shell.db['rootmodules_cache']
|
218 |
+
|
219 |
+
path = [os.path.abspath(os.path.expanduser(p)) for p in
|
220 |
+
os.environ.get('PATH','').split(os.pathsep)]
|
221 |
+
|
222 |
+
syscmdlist = []
|
223 |
+
savedir = os.getcwd()
|
224 |
+
|
225 |
+
# Now walk the paths looking for executables to alias.
|
226 |
+
try:
|
227 |
+
# write the whole loop for posix/Windows so we don't have an if in
|
228 |
+
# the innermost part
|
229 |
+
if self.is_posix:
|
230 |
+
for pdir in path:
|
231 |
+
try:
|
232 |
+
os.chdir(pdir)
|
233 |
+
except OSError:
|
234 |
+
continue
|
235 |
+
|
236 |
+
# for python 3.6+ rewrite to: with os.scandir(pdir) as dirlist:
|
237 |
+
dirlist = os.scandir(path=pdir)
|
238 |
+
for ff in dirlist:
|
239 |
+
if self.isexec(ff):
|
240 |
+
fname = ff.name
|
241 |
+
try:
|
242 |
+
# Removes dots from the name since ipython
|
243 |
+
# will assume names with dots to be python.
|
244 |
+
if not self.shell.alias_manager.is_alias(fname):
|
245 |
+
self.shell.alias_manager.define_alias(
|
246 |
+
fname.replace('.',''), fname)
|
247 |
+
except InvalidAliasError:
|
248 |
+
pass
|
249 |
+
else:
|
250 |
+
syscmdlist.append(fname)
|
251 |
+
else:
|
252 |
+
no_alias = Alias.blacklist
|
253 |
+
for pdir in path:
|
254 |
+
try:
|
255 |
+
os.chdir(pdir)
|
256 |
+
except OSError:
|
257 |
+
continue
|
258 |
+
|
259 |
+
# for python 3.6+ rewrite to: with os.scandir(pdir) as dirlist:
|
260 |
+
dirlist = os.scandir(pdir)
|
261 |
+
for ff in dirlist:
|
262 |
+
fname = ff.name
|
263 |
+
base, ext = os.path.splitext(fname)
|
264 |
+
if self.isexec(ff) and base.lower() not in no_alias:
|
265 |
+
if ext.lower() == '.exe':
|
266 |
+
fname = base
|
267 |
+
try:
|
268 |
+
# Removes dots from the name since ipython
|
269 |
+
# will assume names with dots to be python.
|
270 |
+
self.shell.alias_manager.define_alias(
|
271 |
+
base.lower().replace('.',''), fname)
|
272 |
+
except InvalidAliasError:
|
273 |
+
pass
|
274 |
+
syscmdlist.append(fname)
|
275 |
+
|
276 |
+
self.shell.db['syscmdlist'] = syscmdlist
|
277 |
+
finally:
|
278 |
+
os.chdir(savedir)
|
279 |
+
|
280 |
+
@skip_doctest
|
281 |
+
@line_magic
|
282 |
+
def pwd(self, parameter_s=''):
|
283 |
+
"""Return the current working directory path.
|
284 |
+
|
285 |
+
Examples
|
286 |
+
--------
|
287 |
+
::
|
288 |
+
|
289 |
+
In [9]: pwd
|
290 |
+
Out[9]: '/home/tsuser/sprint/ipython'
|
291 |
+
"""
|
292 |
+
try:
|
293 |
+
return os.getcwd()
|
294 |
+
except FileNotFoundError as e:
|
295 |
+
raise UsageError("CWD no longer exists - please use %cd to change directory.") from e
|
296 |
+
|
297 |
+
@skip_doctest
|
298 |
+
@line_magic
|
299 |
+
def cd(self, parameter_s=''):
|
300 |
+
"""Change the current working directory.
|
301 |
+
|
302 |
+
This command automatically maintains an internal list of directories
|
303 |
+
you visit during your IPython session, in the variable ``_dh``. The
|
304 |
+
command :magic:`%dhist` shows this history nicely formatted. You can
|
305 |
+
also do ``cd -<tab>`` to see directory history conveniently.
|
306 |
+
Usage:
|
307 |
+
|
308 |
+
- ``cd 'dir'``: changes to directory 'dir'.
|
309 |
+
- ``cd -``: changes to the last visited directory.
|
310 |
+
- ``cd -<n>``: changes to the n-th directory in the directory history.
|
311 |
+
- ``cd --foo``: change to directory that matches 'foo' in history
|
312 |
+
- ``cd -b <bookmark_name>``: jump to a bookmark set by %bookmark
|
313 |
+
- Hitting a tab key after ``cd -b`` allows you to tab-complete
|
314 |
+
bookmark names.
|
315 |
+
|
316 |
+
.. note::
|
317 |
+
``cd <bookmark_name>`` is enough if there is no directory
|
318 |
+
``<bookmark_name>``, but a bookmark with the name exists.
|
319 |
+
|
320 |
+
Options:
|
321 |
+
|
322 |
+
-q Be quiet. Do not print the working directory after the
|
323 |
+
cd command is executed. By default IPython's cd
|
324 |
+
command does print this directory, since the default
|
325 |
+
prompts do not display path information.
|
326 |
+
|
327 |
+
.. note::
|
328 |
+
Note that ``!cd`` doesn't work for this purpose because the shell
|
329 |
+
where ``!command`` runs is immediately discarded after executing
|
330 |
+
'command'.
|
331 |
+
|
332 |
+
Examples
|
333 |
+
--------
|
334 |
+
::
|
335 |
+
|
336 |
+
In [10]: cd parent/child
|
337 |
+
/home/tsuser/parent/child
|
338 |
+
"""
|
339 |
+
|
340 |
+
try:
|
341 |
+
oldcwd = os.getcwd()
|
342 |
+
except FileNotFoundError:
|
343 |
+
# Happens if the CWD has been deleted.
|
344 |
+
oldcwd = None
|
345 |
+
|
346 |
+
numcd = re.match(r'(-)(\d+)$',parameter_s)
|
347 |
+
# jump in directory history by number
|
348 |
+
if numcd:
|
349 |
+
nn = int(numcd.group(2))
|
350 |
+
try:
|
351 |
+
ps = self.shell.user_ns['_dh'][nn]
|
352 |
+
except IndexError:
|
353 |
+
print('The requested directory does not exist in history.')
|
354 |
+
return
|
355 |
+
else:
|
356 |
+
opts = {}
|
357 |
+
elif parameter_s.startswith('--'):
|
358 |
+
ps = None
|
359 |
+
fallback = None
|
360 |
+
pat = parameter_s[2:]
|
361 |
+
dh = self.shell.user_ns['_dh']
|
362 |
+
# first search only by basename (last component)
|
363 |
+
for ent in reversed(dh):
|
364 |
+
if pat in os.path.basename(ent) and os.path.isdir(ent):
|
365 |
+
ps = ent
|
366 |
+
break
|
367 |
+
|
368 |
+
if fallback is None and pat in ent and os.path.isdir(ent):
|
369 |
+
fallback = ent
|
370 |
+
|
371 |
+
# if we have no last part match, pick the first full path match
|
372 |
+
if ps is None:
|
373 |
+
ps = fallback
|
374 |
+
|
375 |
+
if ps is None:
|
376 |
+
print("No matching entry in directory history")
|
377 |
+
return
|
378 |
+
else:
|
379 |
+
opts = {}
|
380 |
+
|
381 |
+
|
382 |
+
else:
|
383 |
+
opts, ps = self.parse_options(parameter_s, 'qb', mode='string')
|
384 |
+
# jump to previous
|
385 |
+
if ps == '-':
|
386 |
+
try:
|
387 |
+
ps = self.shell.user_ns['_dh'][-2]
|
388 |
+
except IndexError as e:
|
389 |
+
raise UsageError('%cd -: No previous directory to change to.') from e
|
390 |
+
# jump to bookmark if needed
|
391 |
+
else:
|
392 |
+
if not os.path.isdir(ps) or 'b' in opts:
|
393 |
+
bkms = self.shell.db.get('bookmarks', {})
|
394 |
+
|
395 |
+
if ps in bkms:
|
396 |
+
target = bkms[ps]
|
397 |
+
print('(bookmark:%s) -> %s' % (ps, target))
|
398 |
+
ps = target
|
399 |
+
else:
|
400 |
+
if 'b' in opts:
|
401 |
+
raise UsageError("Bookmark '%s' not found. "
|
402 |
+
"Use '%%bookmark -l' to see your bookmarks." % ps)
|
403 |
+
|
404 |
+
# at this point ps should point to the target dir
|
405 |
+
if ps:
|
406 |
+
try:
|
407 |
+
os.chdir(os.path.expanduser(ps))
|
408 |
+
if hasattr(self.shell, 'term_title') and self.shell.term_title:
|
409 |
+
set_term_title(self.shell.term_title_format.format(cwd=abbrev_cwd()))
|
410 |
+
except OSError:
|
411 |
+
print(sys.exc_info()[1])
|
412 |
+
else:
|
413 |
+
cwd = pathlib.Path.cwd()
|
414 |
+
dhist = self.shell.user_ns['_dh']
|
415 |
+
if oldcwd != cwd:
|
416 |
+
dhist.append(cwd)
|
417 |
+
self.shell.db['dhist'] = compress_dhist(dhist)[-100:]
|
418 |
+
|
419 |
+
else:
|
420 |
+
os.chdir(self.shell.home_dir)
|
421 |
+
if hasattr(self.shell, 'term_title') and self.shell.term_title:
|
422 |
+
set_term_title(self.shell.term_title_format.format(cwd="~"))
|
423 |
+
cwd = pathlib.Path.cwd()
|
424 |
+
dhist = self.shell.user_ns['_dh']
|
425 |
+
|
426 |
+
if oldcwd != cwd:
|
427 |
+
dhist.append(cwd)
|
428 |
+
self.shell.db["dhist"] = compress_dhist(dhist)[-100:]
|
429 |
+
if "q" not in opts and not self.cd_force_quiet and self.shell.user_ns["_dh"]:
|
430 |
+
print(self.shell.user_ns["_dh"][-1])
|
431 |
+
|
432 |
+
@line_magic
|
433 |
+
def env(self, parameter_s=''):
|
434 |
+
"""Get, set, or list environment variables.
|
435 |
+
|
436 |
+
Usage:\\
|
437 |
+
|
438 |
+
:``%env``: lists all environment variables/values
|
439 |
+
:``%env var``: get value for var
|
440 |
+
:``%env var val``: set value for var
|
441 |
+
:``%env var=val``: set value for var
|
442 |
+
:``%env var=$val``: set value for var, using python expansion if possible
|
443 |
+
"""
|
444 |
+
if parameter_s.strip():
|
445 |
+
split = '=' if '=' in parameter_s else ' '
|
446 |
+
bits = parameter_s.split(split)
|
447 |
+
if len(bits) == 1:
|
448 |
+
key = parameter_s.strip()
|
449 |
+
if key in os.environ:
|
450 |
+
return os.environ[key]
|
451 |
+
else:
|
452 |
+
err = "Environment does not have key: {0}".format(key)
|
453 |
+
raise UsageError(err)
|
454 |
+
if len(bits) > 1:
|
455 |
+
return self.set_env(parameter_s)
|
456 |
+
env = dict(os.environ)
|
457 |
+
# hide likely secrets when printing the whole environment
|
458 |
+
for key in list(env):
|
459 |
+
if any(s in key.lower() for s in ('key', 'token', 'secret')):
|
460 |
+
env[key] = '<hidden>'
|
461 |
+
|
462 |
+
return env
|
463 |
+
|
464 |
+
@line_magic
|
465 |
+
def set_env(self, parameter_s):
|
466 |
+
"""Set environment variables. Assumptions are that either "val" is a
|
467 |
+
name in the user namespace, or val is something that evaluates to a
|
468 |
+
string.
|
469 |
+
|
470 |
+
Usage:\\
|
471 |
+
:``%set_env var val``: set value for var
|
472 |
+
:``%set_env var=val``: set value for var
|
473 |
+
:``%set_env var=$val``: set value for var, using python expansion if possible
|
474 |
+
"""
|
475 |
+
split = '=' if '=' in parameter_s else ' '
|
476 |
+
bits = parameter_s.split(split, 1)
|
477 |
+
if not parameter_s.strip() or len(bits)<2:
|
478 |
+
raise UsageError("usage is 'set_env var=val'")
|
479 |
+
var = bits[0].strip()
|
480 |
+
val = bits[1].strip()
|
481 |
+
if re.match(r'.*\s.*', var):
|
482 |
+
# an environment variable with whitespace is almost certainly
|
483 |
+
# not what the user intended. what's more likely is the wrong
|
484 |
+
# split was chosen, ie for "set_env cmd_args A=B", we chose
|
485 |
+
# '=' for the split and should have chosen ' '. to get around
|
486 |
+
# this, users should just assign directly to os.environ or use
|
487 |
+
# standard magic {var} expansion.
|
488 |
+
err = "refusing to set env var with whitespace: '{0}'"
|
489 |
+
err = err.format(val)
|
490 |
+
raise UsageError(err)
|
491 |
+
os.environ[var] = val
|
492 |
+
print('env: {0}={1}'.format(var,val))
|
493 |
+
|
494 |
+
@line_magic
|
495 |
+
def pushd(self, parameter_s=''):
|
496 |
+
"""Place the current dir on stack and change directory.
|
497 |
+
|
498 |
+
Usage:\\
|
499 |
+
%pushd ['dirname']
|
500 |
+
"""
|
501 |
+
|
502 |
+
dir_s = self.shell.dir_stack
|
503 |
+
tgt = os.path.expanduser(parameter_s)
|
504 |
+
cwd = os.getcwd().replace(self.shell.home_dir,'~')
|
505 |
+
if tgt:
|
506 |
+
self.cd(parameter_s)
|
507 |
+
dir_s.insert(0,cwd)
|
508 |
+
return self.shell.run_line_magic('dirs', '')
|
509 |
+
|
510 |
+
@line_magic
|
511 |
+
def popd(self, parameter_s=''):
|
512 |
+
"""Change to directory popped off the top of the stack.
|
513 |
+
"""
|
514 |
+
if not self.shell.dir_stack:
|
515 |
+
raise UsageError("%popd on empty stack")
|
516 |
+
top = self.shell.dir_stack.pop(0)
|
517 |
+
self.cd(top)
|
518 |
+
print("popd ->",top)
|
519 |
+
|
520 |
+
@line_magic
|
521 |
+
def dirs(self, parameter_s=''):
|
522 |
+
"""Return the current directory stack."""
|
523 |
+
|
524 |
+
return self.shell.dir_stack
|
525 |
+
|
526 |
+
@line_magic
|
527 |
+
def dhist(self, parameter_s=''):
|
528 |
+
"""Print your history of visited directories.
|
529 |
+
|
530 |
+
%dhist -> print full history\\
|
531 |
+
%dhist n -> print last n entries only\\
|
532 |
+
%dhist n1 n2 -> print entries between n1 and n2 (n2 not included)\\
|
533 |
+
|
534 |
+
This history is automatically maintained by the %cd command, and
|
535 |
+
always available as the global list variable _dh. You can use %cd -<n>
|
536 |
+
to go to directory number <n>.
|
537 |
+
|
538 |
+
Note that most of time, you should view directory history by entering
|
539 |
+
cd -<TAB>.
|
540 |
+
|
541 |
+
"""
|
542 |
+
|
543 |
+
dh = self.shell.user_ns['_dh']
|
544 |
+
if parameter_s:
|
545 |
+
try:
|
546 |
+
args = map(int,parameter_s.split())
|
547 |
+
except:
|
548 |
+
self.arg_err(self.dhist)
|
549 |
+
return
|
550 |
+
if len(args) == 1:
|
551 |
+
ini,fin = max(len(dh)-(args[0]),0),len(dh)
|
552 |
+
elif len(args) == 2:
|
553 |
+
ini,fin = args
|
554 |
+
fin = min(fin, len(dh))
|
555 |
+
else:
|
556 |
+
self.arg_err(self.dhist)
|
557 |
+
return
|
558 |
+
else:
|
559 |
+
ini,fin = 0,len(dh)
|
560 |
+
print('Directory history (kept in _dh)')
|
561 |
+
for i in range(ini, fin):
|
562 |
+
print("%d: %s" % (i, dh[i]))
|
563 |
+
|
564 |
+
@skip_doctest
|
565 |
+
@line_magic
|
566 |
+
def sc(self, parameter_s=''):
|
567 |
+
"""Shell capture - run shell command and capture output (DEPRECATED use !).
|
568 |
+
|
569 |
+
DEPRECATED. Suboptimal, retained for backwards compatibility.
|
570 |
+
|
571 |
+
You should use the form 'var = !command' instead. Example:
|
572 |
+
|
573 |
+
"%sc -l myfiles = ls ~" should now be written as
|
574 |
+
|
575 |
+
"myfiles = !ls ~"
|
576 |
+
|
577 |
+
myfiles.s, myfiles.l and myfiles.n still apply as documented
|
578 |
+
below.
|
579 |
+
|
580 |
+
--
|
581 |
+
%sc [options] varname=command
|
582 |
+
|
583 |
+
IPython will run the given command using commands.getoutput(), and
|
584 |
+
will then update the user's interactive namespace with a variable
|
585 |
+
called varname, containing the value of the call. Your command can
|
586 |
+
contain shell wildcards, pipes, etc.
|
587 |
+
|
588 |
+
The '=' sign in the syntax is mandatory, and the variable name you
|
589 |
+
supply must follow Python's standard conventions for valid names.
|
590 |
+
|
591 |
+
(A special format without variable name exists for internal use)
|
592 |
+
|
593 |
+
Options:
|
594 |
+
|
595 |
+
-l: list output. Split the output on newlines into a list before
|
596 |
+
assigning it to the given variable. By default the output is stored
|
597 |
+
as a single string.
|
598 |
+
|
599 |
+
-v: verbose. Print the contents of the variable.
|
600 |
+
|
601 |
+
In most cases you should not need to split as a list, because the
|
602 |
+
returned value is a special type of string which can automatically
|
603 |
+
provide its contents either as a list (split on newlines) or as a
|
604 |
+
space-separated string. These are convenient, respectively, either
|
605 |
+
for sequential processing or to be passed to a shell command.
|
606 |
+
|
607 |
+
For example::
|
608 |
+
|
609 |
+
# Capture into variable a
|
610 |
+
In [1]: sc a=ls *py
|
611 |
+
|
612 |
+
# a is a string with embedded newlines
|
613 |
+
In [2]: a
|
614 |
+
Out[2]: 'setup.py\\nwin32_manual_post_install.py'
|
615 |
+
|
616 |
+
# which can be seen as a list:
|
617 |
+
In [3]: a.l
|
618 |
+
Out[3]: ['setup.py', 'win32_manual_post_install.py']
|
619 |
+
|
620 |
+
# or as a whitespace-separated string:
|
621 |
+
In [4]: a.s
|
622 |
+
Out[4]: 'setup.py win32_manual_post_install.py'
|
623 |
+
|
624 |
+
# a.s is useful to pass as a single command line:
|
625 |
+
In [5]: !wc -l $a.s
|
626 |
+
146 setup.py
|
627 |
+
130 win32_manual_post_install.py
|
628 |
+
276 total
|
629 |
+
|
630 |
+
# while the list form is useful to loop over:
|
631 |
+
In [6]: for f in a.l:
|
632 |
+
...: !wc -l $f
|
633 |
+
...:
|
634 |
+
146 setup.py
|
635 |
+
130 win32_manual_post_install.py
|
636 |
+
|
637 |
+
Similarly, the lists returned by the -l option are also special, in
|
638 |
+
the sense that you can equally invoke the .s attribute on them to
|
639 |
+
automatically get a whitespace-separated string from their contents::
|
640 |
+
|
641 |
+
In [7]: sc -l b=ls *py
|
642 |
+
|
643 |
+
In [8]: b
|
644 |
+
Out[8]: ['setup.py', 'win32_manual_post_install.py']
|
645 |
+
|
646 |
+
In [9]: b.s
|
647 |
+
Out[9]: 'setup.py win32_manual_post_install.py'
|
648 |
+
|
649 |
+
In summary, both the lists and strings used for output capture have
|
650 |
+
the following special attributes::
|
651 |
+
|
652 |
+
.l (or .list) : value as list.
|
653 |
+
.n (or .nlstr): value as newline-separated string.
|
654 |
+
.s (or .spstr): value as space-separated string.
|
655 |
+
"""
|
656 |
+
|
657 |
+
opts,args = self.parse_options(parameter_s, 'lv')
|
658 |
+
# Try to get a variable name and command to run
|
659 |
+
try:
|
660 |
+
# the variable name must be obtained from the parse_options
|
661 |
+
# output, which uses shlex.split to strip options out.
|
662 |
+
var,_ = args.split('=', 1)
|
663 |
+
var = var.strip()
|
664 |
+
# But the command has to be extracted from the original input
|
665 |
+
# parameter_s, not on what parse_options returns, to avoid the
|
666 |
+
# quote stripping which shlex.split performs on it.
|
667 |
+
_,cmd = parameter_s.split('=', 1)
|
668 |
+
except ValueError:
|
669 |
+
var,cmd = '',''
|
670 |
+
# If all looks ok, proceed
|
671 |
+
split = 'l' in opts
|
672 |
+
out = self.shell.getoutput(cmd, split=split)
|
673 |
+
if 'v' in opts:
|
674 |
+
print('%s ==\n%s' % (var, pformat(out)))
|
675 |
+
if var:
|
676 |
+
self.shell.user_ns.update({var:out})
|
677 |
+
else:
|
678 |
+
return out
|
679 |
+
|
680 |
+
@line_cell_magic
|
681 |
+
def sx(self, line='', cell=None):
|
682 |
+
"""Shell execute - run shell command and capture output (!! is short-hand).
|
683 |
+
|
684 |
+
%sx command
|
685 |
+
|
686 |
+
IPython will run the given command using commands.getoutput(), and
|
687 |
+
return the result formatted as a list (split on '\\n'). Since the
|
688 |
+
output is _returned_, it will be stored in ipython's regular output
|
689 |
+
cache Out[N] and in the '_N' automatic variables.
|
690 |
+
|
691 |
+
Notes:
|
692 |
+
|
693 |
+
1) If an input line begins with '!!', then %sx is automatically
|
694 |
+
invoked. That is, while::
|
695 |
+
|
696 |
+
!ls
|
697 |
+
|
698 |
+
causes ipython to simply issue system('ls'), typing::
|
699 |
+
|
700 |
+
!!ls
|
701 |
+
|
702 |
+
is a shorthand equivalent to::
|
703 |
+
|
704 |
+
%sx ls
|
705 |
+
|
706 |
+
2) %sx differs from %sc in that %sx automatically splits into a list,
|
707 |
+
like '%sc -l'. The reason for this is to make it as easy as possible
|
708 |
+
to process line-oriented shell output via further python commands.
|
709 |
+
%sc is meant to provide much finer control, but requires more
|
710 |
+
typing.
|
711 |
+
|
712 |
+
3) Just like %sc -l, this is a list with special attributes:
|
713 |
+
::
|
714 |
+
|
715 |
+
.l (or .list) : value as list.
|
716 |
+
.n (or .nlstr): value as newline-separated string.
|
717 |
+
.s (or .spstr): value as whitespace-separated string.
|
718 |
+
|
719 |
+
This is very useful when trying to use such lists as arguments to
|
720 |
+
system commands."""
|
721 |
+
|
722 |
+
if cell is None:
|
723 |
+
# line magic
|
724 |
+
return self.shell.getoutput(line)
|
725 |
+
else:
|
726 |
+
opts,args = self.parse_options(line, '', 'out=')
|
727 |
+
output = self.shell.getoutput(cell)
|
728 |
+
out_name = opts.get('out', opts.get('o'))
|
729 |
+
if out_name:
|
730 |
+
self.shell.user_ns[out_name] = output
|
731 |
+
else:
|
732 |
+
return output
|
733 |
+
|
734 |
+
system = line_cell_magic('system')(sx)
|
735 |
+
bang = cell_magic('!')(sx)
|
736 |
+
|
737 |
+
@line_magic
|
738 |
+
def bookmark(self, parameter_s=''):
|
739 |
+
"""Manage IPython's bookmark system.
|
740 |
+
|
741 |
+
%bookmark <name> - set bookmark to current dir
|
742 |
+
%bookmark <name> <dir> - set bookmark to <dir>
|
743 |
+
%bookmark -l - list all bookmarks
|
744 |
+
%bookmark -d <name> - remove bookmark
|
745 |
+
%bookmark -r - remove all bookmarks
|
746 |
+
|
747 |
+
You can later on access a bookmarked folder with::
|
748 |
+
|
749 |
+
%cd -b <name>
|
750 |
+
|
751 |
+
or simply '%cd <name>' if there is no directory called <name> AND
|
752 |
+
there is such a bookmark defined.
|
753 |
+
|
754 |
+
Your bookmarks persist through IPython sessions, but they are
|
755 |
+
associated with each profile."""
|
756 |
+
|
757 |
+
opts,args = self.parse_options(parameter_s,'drl',mode='list')
|
758 |
+
if len(args) > 2:
|
759 |
+
raise UsageError("%bookmark: too many arguments")
|
760 |
+
|
761 |
+
bkms = self.shell.db.get('bookmarks',{})
|
762 |
+
|
763 |
+
if 'd' in opts:
|
764 |
+
try:
|
765 |
+
todel = args[0]
|
766 |
+
except IndexError as e:
|
767 |
+
raise UsageError(
|
768 |
+
"%bookmark -d: must provide a bookmark to delete") from e
|
769 |
+
else:
|
770 |
+
try:
|
771 |
+
del bkms[todel]
|
772 |
+
except KeyError as e:
|
773 |
+
raise UsageError(
|
774 |
+
"%%bookmark -d: Can't delete bookmark '%s'" % todel) from e
|
775 |
+
|
776 |
+
elif 'r' in opts:
|
777 |
+
bkms = {}
|
778 |
+
elif 'l' in opts:
|
779 |
+
bks = sorted(bkms)
|
780 |
+
if bks:
|
781 |
+
size = max(map(len, bks))
|
782 |
+
else:
|
783 |
+
size = 0
|
784 |
+
fmt = '%-'+str(size)+'s -> %s'
|
785 |
+
print('Current bookmarks:')
|
786 |
+
for bk in bks:
|
787 |
+
print(fmt % (bk, bkms[bk]))
|
788 |
+
else:
|
789 |
+
if not args:
|
790 |
+
raise UsageError("%bookmark: You must specify the bookmark name")
|
791 |
+
elif len(args)==1:
|
792 |
+
bkms[args[0]] = os.getcwd()
|
793 |
+
elif len(args)==2:
|
794 |
+
bkms[args[0]] = args[1]
|
795 |
+
self.shell.db['bookmarks'] = bkms
|
796 |
+
|
797 |
+
@line_magic
|
798 |
+
def pycat(self, parameter_s=''):
|
799 |
+
"""Show a syntax-highlighted file through a pager.
|
800 |
+
|
801 |
+
This magic is similar to the cat utility, but it will assume the file
|
802 |
+
to be Python source and will show it with syntax highlighting.
|
803 |
+
|
804 |
+
This magic command can either take a local filename, an url,
|
805 |
+
an history range (see %history) or a macro as argument.
|
806 |
+
|
807 |
+
If no parameter is given, prints out history of current session up to
|
808 |
+
this point. ::
|
809 |
+
|
810 |
+
%pycat myscript.py
|
811 |
+
%pycat 7-27
|
812 |
+
%pycat myMacro
|
813 |
+
%pycat http://www.example.com/myscript.py
|
814 |
+
"""
|
815 |
+
try:
|
816 |
+
cont = self.shell.find_user_code(parameter_s, skip_encoding_cookie=False)
|
817 |
+
except (ValueError, IOError):
|
818 |
+
print("Error: no such file, variable, URL, history range or macro")
|
819 |
+
return
|
820 |
+
|
821 |
+
page.page(self.shell.pycolorize(source_to_unicode(cont)))
|
822 |
+
|
823 |
+
@magic_arguments.magic_arguments()
|
824 |
+
@magic_arguments.argument(
|
825 |
+
'-a', '--append', action='store_true', default=False,
|
826 |
+
help='Append contents of the cell to an existing file. '
|
827 |
+
'The file will be created if it does not exist.'
|
828 |
+
)
|
829 |
+
@magic_arguments.argument(
|
830 |
+
'filename', type=str,
|
831 |
+
help='file to write'
|
832 |
+
)
|
833 |
+
@cell_magic
|
834 |
+
def writefile(self, line, cell):
|
835 |
+
"""Write the contents of the cell to a file.
|
836 |
+
|
837 |
+
The file will be overwritten unless the -a (--append) flag is specified.
|
838 |
+
"""
|
839 |
+
args = magic_arguments.parse_argstring(self.writefile, line)
|
840 |
+
if re.match(r'^(\'.*\')|(".*")$', args.filename):
|
841 |
+
filename = os.path.expanduser(args.filename[1:-1])
|
842 |
+
else:
|
843 |
+
filename = os.path.expanduser(args.filename)
|
844 |
+
|
845 |
+
if os.path.exists(filename):
|
846 |
+
if args.append:
|
847 |
+
print("Appending to %s" % filename)
|
848 |
+
else:
|
849 |
+
print("Overwriting %s" % filename)
|
850 |
+
else:
|
851 |
+
print("Writing %s" % filename)
|
852 |
+
|
853 |
+
mode = 'a' if args.append else 'w'
|
854 |
+
with io.open(filename, mode, encoding='utf-8') as f:
|
855 |
+
f.write(cell)
|
temp_venv/lib/python3.13/site-packages/IPython/core/magics/packaging.py
ADDED
@@ -0,0 +1,181 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Implementation of packaging-related magic functions.
|
2 |
+
"""
|
3 |
+
#-----------------------------------------------------------------------------
|
4 |
+
# Copyright (c) 2018 The IPython Development Team.
|
5 |
+
#
|
6 |
+
# Distributed under the terms of the Modified BSD License.
|
7 |
+
#
|
8 |
+
# The full license is in the file COPYING.txt, distributed with this software.
|
9 |
+
#-----------------------------------------------------------------------------
|
10 |
+
|
11 |
+
import functools
|
12 |
+
import os
|
13 |
+
import re
|
14 |
+
import shlex
|
15 |
+
import sys
|
16 |
+
from pathlib import Path
|
17 |
+
|
18 |
+
from IPython.core.magic import Magics, magics_class, line_magic
|
19 |
+
|
20 |
+
|
21 |
+
def is_conda_environment(func):
|
22 |
+
@functools.wraps(func)
|
23 |
+
def wrapper(*args, **kwargs):
|
24 |
+
"""Return True if the current Python executable is in a conda env"""
|
25 |
+
# TODO: does this need to change on windows?
|
26 |
+
if not Path(sys.prefix, "conda-meta", "history").exists():
|
27 |
+
raise ValueError(
|
28 |
+
"The python kernel does not appear to be a conda environment. "
|
29 |
+
"Please use ``%pip install`` instead."
|
30 |
+
)
|
31 |
+
return func(*args, **kwargs)
|
32 |
+
|
33 |
+
return wrapper
|
34 |
+
|
35 |
+
|
36 |
+
def _get_conda_like_executable(command):
|
37 |
+
"""Find the path to the given executable
|
38 |
+
|
39 |
+
Parameters
|
40 |
+
----------
|
41 |
+
|
42 |
+
executable: string
|
43 |
+
Value should be: conda, mamba or micromamba
|
44 |
+
"""
|
45 |
+
# Check for a environment variable bound to the base executable, both conda and mamba
|
46 |
+
# set these when activating an environment.
|
47 |
+
base_executable = "CONDA_EXE"
|
48 |
+
if "mamba" in command.lower():
|
49 |
+
base_executable = "MAMBA_EXE"
|
50 |
+
if base_executable in os.environ:
|
51 |
+
executable = Path(os.environ[base_executable])
|
52 |
+
if executable.is_file():
|
53 |
+
return str(executable.resolve())
|
54 |
+
|
55 |
+
# Check if there is a conda executable in the same directory as the Python executable.
|
56 |
+
# This is the case within conda's root environment.
|
57 |
+
executable = Path(sys.executable).parent / command
|
58 |
+
if executable.is_file():
|
59 |
+
return str(executable)
|
60 |
+
|
61 |
+
# Otherwise, attempt to extract the executable from conda history.
|
62 |
+
# This applies in any conda environment. Parsing this way is error prone because
|
63 |
+
# different versions of conda and mamba include differing cmd values such as
|
64 |
+
# `conda`, `conda-script.py`, or `path/to/conda`, here use the raw command provided.
|
65 |
+
history = Path(sys.prefix, "conda-meta", "history").read_text(encoding="utf-8")
|
66 |
+
match = re.search(
|
67 |
+
rf"^#\s*cmd:\s*(?P<command>.*{command})\s[create|install]",
|
68 |
+
history,
|
69 |
+
flags=re.MULTILINE,
|
70 |
+
)
|
71 |
+
if match:
|
72 |
+
return match.groupdict()["command"]
|
73 |
+
|
74 |
+
# Fallback: assume the executable is available on the system path.
|
75 |
+
return command
|
76 |
+
|
77 |
+
|
78 |
+
CONDA_COMMANDS_REQUIRING_PREFIX = {
|
79 |
+
'install', 'list', 'remove', 'uninstall', 'update', 'upgrade',
|
80 |
+
}
|
81 |
+
CONDA_COMMANDS_REQUIRING_YES = {
|
82 |
+
'install', 'remove', 'uninstall', 'update', 'upgrade',
|
83 |
+
}
|
84 |
+
CONDA_ENV_FLAGS = {'-p', '--prefix', '-n', '--name'}
|
85 |
+
CONDA_YES_FLAGS = {'-y', '--y'}
|
86 |
+
|
87 |
+
|
88 |
+
@magics_class
|
89 |
+
class PackagingMagics(Magics):
|
90 |
+
"""Magics related to packaging & installation"""
|
91 |
+
|
92 |
+
@line_magic
|
93 |
+
def pip(self, line):
|
94 |
+
"""Run the pip package manager within the current kernel.
|
95 |
+
|
96 |
+
Usage:
|
97 |
+
%pip install [pkgs]
|
98 |
+
"""
|
99 |
+
python = sys.executable
|
100 |
+
if sys.platform == "win32":
|
101 |
+
python = '"' + python + '"'
|
102 |
+
else:
|
103 |
+
python = shlex.quote(python)
|
104 |
+
|
105 |
+
self.shell.system(" ".join([python, "-m", "pip", line]))
|
106 |
+
|
107 |
+
print("Note: you may need to restart the kernel to use updated packages.")
|
108 |
+
|
109 |
+
def _run_command(self, cmd, line):
|
110 |
+
args = shlex.split(line)
|
111 |
+
command = args[0] if len(args) > 0 else ""
|
112 |
+
args = args[1:] if len(args) > 1 else [""]
|
113 |
+
|
114 |
+
extra_args = []
|
115 |
+
|
116 |
+
# When the subprocess does not allow us to respond "yes" during the installation,
|
117 |
+
# we need to insert --yes in the argument list for some commands
|
118 |
+
stdin_disabled = getattr(self.shell, 'kernel', None) is not None
|
119 |
+
needs_yes = command in CONDA_COMMANDS_REQUIRING_YES
|
120 |
+
has_yes = set(args).intersection(CONDA_YES_FLAGS)
|
121 |
+
if stdin_disabled and needs_yes and not has_yes:
|
122 |
+
extra_args.append("--yes")
|
123 |
+
|
124 |
+
# Add --prefix to point conda installation to the current environment
|
125 |
+
needs_prefix = command in CONDA_COMMANDS_REQUIRING_PREFIX
|
126 |
+
has_prefix = set(args).intersection(CONDA_ENV_FLAGS)
|
127 |
+
if needs_prefix and not has_prefix:
|
128 |
+
extra_args.extend(["--prefix", sys.prefix])
|
129 |
+
|
130 |
+
self.shell.system(" ".join([cmd, command] + extra_args + args))
|
131 |
+
print("\nNote: you may need to restart the kernel to use updated packages.")
|
132 |
+
|
133 |
+
@line_magic
|
134 |
+
@is_conda_environment
|
135 |
+
def conda(self, line):
|
136 |
+
"""Run the conda package manager within the current kernel.
|
137 |
+
|
138 |
+
Usage:
|
139 |
+
%conda install [pkgs]
|
140 |
+
"""
|
141 |
+
conda = _get_conda_like_executable("conda")
|
142 |
+
self._run_command(conda, line)
|
143 |
+
|
144 |
+
@line_magic
|
145 |
+
@is_conda_environment
|
146 |
+
def mamba(self, line):
|
147 |
+
"""Run the mamba package manager within the current kernel.
|
148 |
+
|
149 |
+
Usage:
|
150 |
+
%mamba install [pkgs]
|
151 |
+
"""
|
152 |
+
mamba = _get_conda_like_executable("mamba")
|
153 |
+
self._run_command(mamba, line)
|
154 |
+
|
155 |
+
@line_magic
|
156 |
+
@is_conda_environment
|
157 |
+
def micromamba(self, line):
|
158 |
+
"""Run the conda package manager within the current kernel.
|
159 |
+
|
160 |
+
Usage:
|
161 |
+
%micromamba install [pkgs]
|
162 |
+
"""
|
163 |
+
micromamba = _get_conda_like_executable("micromamba")
|
164 |
+
self._run_command(micromamba, line)
|
165 |
+
|
166 |
+
@line_magic
|
167 |
+
def uv(self, line):
|
168 |
+
"""Run the uv package manager within the current kernel.
|
169 |
+
|
170 |
+
Usage:
|
171 |
+
%uv pip install [pkgs]
|
172 |
+
"""
|
173 |
+
python = sys.executable
|
174 |
+
if sys.platform == "win32":
|
175 |
+
python = '"' + python + '"'
|
176 |
+
else:
|
177 |
+
python = shlex.quote(python)
|
178 |
+
|
179 |
+
self.shell.system(" ".join([python, "-m", "uv", line]))
|
180 |
+
|
181 |
+
print("Note: you may need to restart the kernel to use updated packages.")
|
temp_venv/lib/python3.13/site-packages/IPython/core/magics/pylab.py
ADDED
@@ -0,0 +1,173 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Implementation of magic functions for matplotlib/pylab support.
|
2 |
+
"""
|
3 |
+
#-----------------------------------------------------------------------------
|
4 |
+
# Copyright (c) 2012 The IPython Development Team.
|
5 |
+
#
|
6 |
+
# Distributed under the terms of the Modified BSD License.
|
7 |
+
#
|
8 |
+
# The full license is in the file COPYING.txt, distributed with this software.
|
9 |
+
#-----------------------------------------------------------------------------
|
10 |
+
|
11 |
+
#-----------------------------------------------------------------------------
|
12 |
+
# Imports
|
13 |
+
#-----------------------------------------------------------------------------
|
14 |
+
|
15 |
+
# Our own packages
|
16 |
+
from traitlets.config.application import Application
|
17 |
+
from IPython.core import magic_arguments
|
18 |
+
from IPython.core.magic import Magics, magics_class, line_magic
|
19 |
+
from IPython.testing.skipdoctest import skip_doctest
|
20 |
+
from warnings import warn
|
21 |
+
|
22 |
+
#-----------------------------------------------------------------------------
|
23 |
+
# Magic implementation classes
|
24 |
+
#-----------------------------------------------------------------------------
|
25 |
+
|
26 |
+
magic_gui_arg = magic_arguments.argument(
|
27 |
+
"gui",
|
28 |
+
nargs="?",
|
29 |
+
help="""Name of the matplotlib backend to use such as 'qt' or 'widget'.
|
30 |
+
If given, the corresponding matplotlib backend is used,
|
31 |
+
otherwise it will be matplotlib's default
|
32 |
+
(which you can set in your matplotlib config file).
|
33 |
+
""",
|
34 |
+
)
|
35 |
+
|
36 |
+
|
37 |
+
@magics_class
|
38 |
+
class PylabMagics(Magics):
|
39 |
+
"""Magics related to matplotlib's pylab support"""
|
40 |
+
|
41 |
+
@skip_doctest
|
42 |
+
@line_magic
|
43 |
+
@magic_arguments.magic_arguments()
|
44 |
+
@magic_arguments.argument('-l', '--list', action='store_true',
|
45 |
+
help='Show available matplotlib backends')
|
46 |
+
@magic_gui_arg
|
47 |
+
def matplotlib(self, line=''):
|
48 |
+
"""Set up matplotlib to work interactively.
|
49 |
+
|
50 |
+
This function lets you activate matplotlib interactive support
|
51 |
+
at any point during an IPython session. It does not import anything
|
52 |
+
into the interactive namespace.
|
53 |
+
|
54 |
+
If you are using the inline matplotlib backend in the IPython Notebook
|
55 |
+
you can set which figure formats are enabled using the following::
|
56 |
+
|
57 |
+
In [1]: from matplotlib_inline.backend_inline import set_matplotlib_formats
|
58 |
+
|
59 |
+
In [2]: set_matplotlib_formats('pdf', 'svg')
|
60 |
+
|
61 |
+
The default for inline figures sets `bbox_inches` to 'tight'. This can
|
62 |
+
cause discrepancies between the displayed image and the identical
|
63 |
+
image created using `savefig`. This behavior can be disabled using the
|
64 |
+
`%config` magic::
|
65 |
+
|
66 |
+
In [3]: %config InlineBackend.print_figure_kwargs = {'bbox_inches':None}
|
67 |
+
|
68 |
+
In addition, see the docstrings of
|
69 |
+
`matplotlib_inline.backend_inline.set_matplotlib_formats` and
|
70 |
+
`matplotlib_inline.backend_inline.set_matplotlib_close` for more information on
|
71 |
+
changing additional behaviors of the inline backend.
|
72 |
+
|
73 |
+
Examples
|
74 |
+
--------
|
75 |
+
To enable the inline backend for usage with the IPython Notebook::
|
76 |
+
|
77 |
+
In [1]: %matplotlib inline
|
78 |
+
|
79 |
+
In this case, where the matplotlib default is TkAgg::
|
80 |
+
|
81 |
+
In [2]: %matplotlib
|
82 |
+
Using matplotlib backend: TkAgg
|
83 |
+
|
84 |
+
But you can explicitly request a different GUI backend::
|
85 |
+
|
86 |
+
In [3]: %matplotlib qt
|
87 |
+
|
88 |
+
You can list the available backends using the -l/--list option::
|
89 |
+
|
90 |
+
In [4]: %matplotlib --list
|
91 |
+
Available matplotlib backends: ['osx', 'qt4', 'qt5', 'gtk3', 'gtk4', 'notebook', 'wx', 'qt', 'nbagg',
|
92 |
+
'gtk', 'tk', 'inline']
|
93 |
+
"""
|
94 |
+
args = magic_arguments.parse_argstring(self.matplotlib, line)
|
95 |
+
if args.list:
|
96 |
+
from IPython.core.pylabtools import _list_matplotlib_backends_and_gui_loops
|
97 |
+
|
98 |
+
print(
|
99 |
+
"Available matplotlib backends: %s"
|
100 |
+
% _list_matplotlib_backends_and_gui_loops()
|
101 |
+
)
|
102 |
+
else:
|
103 |
+
gui, backend = self.shell.enable_matplotlib(args.gui)
|
104 |
+
self._show_matplotlib_backend(args.gui, backend)
|
105 |
+
|
106 |
+
@skip_doctest
|
107 |
+
@line_magic
|
108 |
+
@magic_arguments.magic_arguments()
|
109 |
+
@magic_arguments.argument(
|
110 |
+
'--no-import-all', action='store_true', default=None,
|
111 |
+
help="""Prevent IPython from performing ``import *`` into the interactive namespace.
|
112 |
+
|
113 |
+
You can govern the default behavior of this flag with the
|
114 |
+
InteractiveShellApp.pylab_import_all configurable.
|
115 |
+
"""
|
116 |
+
)
|
117 |
+
@magic_gui_arg
|
118 |
+
def pylab(self, line=''):
|
119 |
+
"""Load numpy and matplotlib to work interactively.
|
120 |
+
|
121 |
+
This function lets you activate pylab (matplotlib, numpy and
|
122 |
+
interactive support) at any point during an IPython session.
|
123 |
+
|
124 |
+
%pylab makes the following imports::
|
125 |
+
|
126 |
+
import numpy
|
127 |
+
import matplotlib
|
128 |
+
from matplotlib import pylab, mlab, pyplot
|
129 |
+
np = numpy
|
130 |
+
plt = pyplot
|
131 |
+
|
132 |
+
from IPython.display import display
|
133 |
+
from IPython.core.pylabtools import figsize, getfigs
|
134 |
+
|
135 |
+
from pylab import *
|
136 |
+
from numpy import *
|
137 |
+
|
138 |
+
If you pass `--no-import-all`, the last two `*` imports will be excluded.
|
139 |
+
|
140 |
+
See the %matplotlib magic for more details about activating matplotlib
|
141 |
+
without affecting the interactive namespace.
|
142 |
+
"""
|
143 |
+
args = magic_arguments.parse_argstring(self.pylab, line)
|
144 |
+
if args.no_import_all is None:
|
145 |
+
# get default from Application
|
146 |
+
if Application.initialized():
|
147 |
+
app = Application.instance()
|
148 |
+
try:
|
149 |
+
import_all = app.pylab_import_all
|
150 |
+
except AttributeError:
|
151 |
+
import_all = True
|
152 |
+
else:
|
153 |
+
# nothing specified, no app - default True
|
154 |
+
import_all = True
|
155 |
+
else:
|
156 |
+
# invert no-import flag
|
157 |
+
import_all = not args.no_import_all
|
158 |
+
|
159 |
+
gui, backend, clobbered = self.shell.enable_pylab(args.gui, import_all=import_all)
|
160 |
+
self._show_matplotlib_backend(args.gui, backend)
|
161 |
+
print(
|
162 |
+
"%pylab is deprecated, use %matplotlib inline and import the required libraries."
|
163 |
+
)
|
164 |
+
print("Populating the interactive namespace from numpy and matplotlib")
|
165 |
+
if clobbered:
|
166 |
+
warn("pylab import has clobbered these variables: %s" % clobbered +
|
167 |
+
"\n`%matplotlib` prevents importing * from pylab and numpy"
|
168 |
+
)
|
169 |
+
|
170 |
+
def _show_matplotlib_backend(self, gui, backend):
|
171 |
+
"""show matplotlib message backend message"""
|
172 |
+
if not gui or gui == 'auto':
|
173 |
+
print("Using matplotlib backend: %s" % backend)
|
temp_venv/lib/python3.13/site-packages/IPython/core/magics/script.py
ADDED
@@ -0,0 +1,393 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Magic functions for running cells in various scripts."""
|
2 |
+
|
3 |
+
# Copyright (c) IPython Development Team.
|
4 |
+
# Distributed under the terms of the Modified BSD License.
|
5 |
+
|
6 |
+
import asyncio
|
7 |
+
import asyncio.exceptions
|
8 |
+
import atexit
|
9 |
+
import errno
|
10 |
+
import os
|
11 |
+
import signal
|
12 |
+
import sys
|
13 |
+
import time
|
14 |
+
from subprocess import CalledProcessError
|
15 |
+
from threading import Thread
|
16 |
+
|
17 |
+
from traitlets import Any, Dict, List, default
|
18 |
+
|
19 |
+
from IPython.core import magic_arguments
|
20 |
+
from IPython.core.async_helpers import _AsyncIOProxy
|
21 |
+
from IPython.core.magic import Magics, cell_magic, line_magic, magics_class
|
22 |
+
from IPython.utils.process import arg_split
|
23 |
+
|
24 |
+
#-----------------------------------------------------------------------------
|
25 |
+
# Magic implementation classes
|
26 |
+
#-----------------------------------------------------------------------------
|
27 |
+
|
28 |
+
def script_args(f):
|
29 |
+
"""single decorator for adding script args"""
|
30 |
+
args = [
|
31 |
+
magic_arguments.argument(
|
32 |
+
'--out', type=str,
|
33 |
+
help="""The variable in which to store stdout from the script.
|
34 |
+
If the script is backgrounded, this will be the stdout *pipe*,
|
35 |
+
instead of the stderr text itself and will not be auto closed.
|
36 |
+
"""
|
37 |
+
),
|
38 |
+
magic_arguments.argument(
|
39 |
+
'--err', type=str,
|
40 |
+
help="""The variable in which to store stderr from the script.
|
41 |
+
If the script is backgrounded, this will be the stderr *pipe*,
|
42 |
+
instead of the stderr text itself and will not be autoclosed.
|
43 |
+
"""
|
44 |
+
),
|
45 |
+
magic_arguments.argument(
|
46 |
+
'--bg', action="store_true",
|
47 |
+
help="""Whether to run the script in the background.
|
48 |
+
If given, the only way to see the output of the command is
|
49 |
+
with --out/err.
|
50 |
+
"""
|
51 |
+
),
|
52 |
+
magic_arguments.argument(
|
53 |
+
'--proc', type=str,
|
54 |
+
help="""The variable in which to store Popen instance.
|
55 |
+
This is used only when --bg option is given.
|
56 |
+
"""
|
57 |
+
),
|
58 |
+
magic_arguments.argument(
|
59 |
+
'--no-raise-error', action="store_false", dest='raise_error',
|
60 |
+
help="""Whether you should raise an error message in addition to
|
61 |
+
a stream on stderr if you get a nonzero exit code.
|
62 |
+
""",
|
63 |
+
),
|
64 |
+
]
|
65 |
+
for arg in args:
|
66 |
+
f = arg(f)
|
67 |
+
return f
|
68 |
+
|
69 |
+
|
70 |
+
class RaiseAfterInterrupt(Exception):
|
71 |
+
pass
|
72 |
+
|
73 |
+
|
74 |
+
@magics_class
|
75 |
+
class ScriptMagics(Magics):
|
76 |
+
"""Magics for talking to scripts
|
77 |
+
|
78 |
+
This defines a base `%%script` cell magic for running a cell
|
79 |
+
with a program in a subprocess, and registers a few top-level
|
80 |
+
magics that call %%script with common interpreters.
|
81 |
+
"""
|
82 |
+
|
83 |
+
event_loop = Any(
|
84 |
+
help="""
|
85 |
+
The event loop on which to run subprocesses
|
86 |
+
|
87 |
+
Not the main event loop,
|
88 |
+
because we want to be able to make blocking calls
|
89 |
+
and have certain requirements we don't want to impose on the main loop.
|
90 |
+
"""
|
91 |
+
)
|
92 |
+
|
93 |
+
script_magics: List = List(
|
94 |
+
help="""Extra script cell magics to define
|
95 |
+
|
96 |
+
This generates simple wrappers of `%%script foo` as `%%foo`.
|
97 |
+
|
98 |
+
If you want to add script magics that aren't on your path,
|
99 |
+
specify them in script_paths
|
100 |
+
""",
|
101 |
+
).tag(config=True)
|
102 |
+
|
103 |
+
@default('script_magics')
|
104 |
+
def _script_magics_default(self):
|
105 |
+
"""default to a common list of programs"""
|
106 |
+
|
107 |
+
defaults = [
|
108 |
+
'sh',
|
109 |
+
'bash',
|
110 |
+
'perl',
|
111 |
+
'ruby',
|
112 |
+
'python',
|
113 |
+
'python2',
|
114 |
+
'python3',
|
115 |
+
'pypy',
|
116 |
+
]
|
117 |
+
if os.name == 'nt':
|
118 |
+
defaults.extend([
|
119 |
+
'cmd',
|
120 |
+
])
|
121 |
+
|
122 |
+
return defaults
|
123 |
+
|
124 |
+
script_paths = Dict(
|
125 |
+
help="""Dict mapping short 'ruby' names to full paths, such as '/opt/secret/bin/ruby'
|
126 |
+
|
127 |
+
Only necessary for items in script_magics where the default path will not
|
128 |
+
find the right interpreter.
|
129 |
+
"""
|
130 |
+
).tag(config=True)
|
131 |
+
|
132 |
+
def __init__(self, shell=None):
|
133 |
+
super(ScriptMagics, self).__init__(shell=shell)
|
134 |
+
self._generate_script_magics()
|
135 |
+
self.bg_processes = []
|
136 |
+
atexit.register(self.kill_bg_processes)
|
137 |
+
|
138 |
+
def __del__(self):
|
139 |
+
self.kill_bg_processes()
|
140 |
+
|
141 |
+
def _generate_script_magics(self):
|
142 |
+
cell_magics = self.magics['cell']
|
143 |
+
for name in self.script_magics:
|
144 |
+
cell_magics[name] = self._make_script_magic(name)
|
145 |
+
|
146 |
+
def _make_script_magic(self, name):
|
147 |
+
"""make a named magic, that calls %%script with a particular program"""
|
148 |
+
# expand to explicit path if necessary:
|
149 |
+
script = self.script_paths.get(name, name)
|
150 |
+
|
151 |
+
@magic_arguments.magic_arguments()
|
152 |
+
@script_args
|
153 |
+
def named_script_magic(line, cell):
|
154 |
+
# if line, add it as cl-flags
|
155 |
+
if line:
|
156 |
+
line = "%s %s" % (script, line)
|
157 |
+
else:
|
158 |
+
line = script
|
159 |
+
return self.shebang(line, cell)
|
160 |
+
|
161 |
+
# write a basic docstring:
|
162 |
+
named_script_magic.__doc__ = \
|
163 |
+
"""%%{name} script magic
|
164 |
+
|
165 |
+
Run cells with {script} in a subprocess.
|
166 |
+
|
167 |
+
This is a shortcut for `%%script {script}`
|
168 |
+
""".format(**locals())
|
169 |
+
|
170 |
+
return named_script_magic
|
171 |
+
|
172 |
+
@magic_arguments.magic_arguments()
|
173 |
+
@script_args
|
174 |
+
@cell_magic("script")
|
175 |
+
def shebang(self, line, cell):
|
176 |
+
"""Run a cell via a shell command
|
177 |
+
|
178 |
+
The `%%script` line is like the #! line of script,
|
179 |
+
specifying a program (bash, perl, ruby, etc.) with which to run.
|
180 |
+
|
181 |
+
The rest of the cell is run by that program.
|
182 |
+
|
183 |
+
.. versionchanged:: 9.0
|
184 |
+
Interrupting the script executed without `--bg` will end in
|
185 |
+
raising an exception (unless `--no-raise-error` is passed).
|
186 |
+
|
187 |
+
Examples
|
188 |
+
--------
|
189 |
+
::
|
190 |
+
|
191 |
+
In [1]: %%script bash
|
192 |
+
...: for i in 1 2 3; do
|
193 |
+
...: echo $i
|
194 |
+
...: done
|
195 |
+
1
|
196 |
+
2
|
197 |
+
3
|
198 |
+
"""
|
199 |
+
|
200 |
+
# Create the event loop in which to run script magics
|
201 |
+
# this operates on a background thread
|
202 |
+
if self.event_loop is None:
|
203 |
+
if sys.platform == "win32":
|
204 |
+
# don't override the current policy,
|
205 |
+
# just create an event loop
|
206 |
+
event_loop = asyncio.WindowsProactorEventLoopPolicy().new_event_loop()
|
207 |
+
else:
|
208 |
+
event_loop = asyncio.new_event_loop()
|
209 |
+
self.event_loop = event_loop
|
210 |
+
|
211 |
+
# start the loop in a background thread
|
212 |
+
asyncio_thread = Thread(target=event_loop.run_forever, daemon=True)
|
213 |
+
asyncio_thread.start()
|
214 |
+
else:
|
215 |
+
event_loop = self.event_loop
|
216 |
+
|
217 |
+
def in_thread(coro):
|
218 |
+
"""Call a coroutine on the asyncio thread"""
|
219 |
+
return asyncio.run_coroutine_threadsafe(coro, event_loop).result()
|
220 |
+
|
221 |
+
async def _readchunk(stream):
|
222 |
+
try:
|
223 |
+
return await stream.read(100)
|
224 |
+
except asyncio.exceptions.IncompleteReadError as e:
|
225 |
+
return e.partial
|
226 |
+
except asyncio.exceptions.LimitOverrunError as e:
|
227 |
+
return await stream.read(e.consumed)
|
228 |
+
|
229 |
+
async def _handle_stream(stream, stream_arg, file_object):
|
230 |
+
while True:
|
231 |
+
chunk = (await _readchunk(stream)).decode("utf8", errors="replace")
|
232 |
+
if not chunk:
|
233 |
+
break
|
234 |
+
if stream_arg:
|
235 |
+
self.shell.user_ns[stream_arg] = chunk
|
236 |
+
else:
|
237 |
+
file_object.write(chunk)
|
238 |
+
file_object.flush()
|
239 |
+
|
240 |
+
async def _stream_communicate(process, cell):
|
241 |
+
process.stdin.write(cell)
|
242 |
+
process.stdin.close()
|
243 |
+
stdout_task = asyncio.create_task(
|
244 |
+
_handle_stream(process.stdout, args.out, sys.stdout)
|
245 |
+
)
|
246 |
+
stderr_task = asyncio.create_task(
|
247 |
+
_handle_stream(process.stderr, args.err, sys.stderr)
|
248 |
+
)
|
249 |
+
await asyncio.wait([stdout_task, stderr_task])
|
250 |
+
await process.wait()
|
251 |
+
|
252 |
+
argv = arg_split(line, posix=not sys.platform.startswith("win"))
|
253 |
+
args, cmd = self.shebang.parser.parse_known_args(argv)
|
254 |
+
|
255 |
+
try:
|
256 |
+
p = in_thread(
|
257 |
+
asyncio.create_subprocess_exec(
|
258 |
+
*cmd,
|
259 |
+
stdout=asyncio.subprocess.PIPE,
|
260 |
+
stderr=asyncio.subprocess.PIPE,
|
261 |
+
stdin=asyncio.subprocess.PIPE,
|
262 |
+
)
|
263 |
+
)
|
264 |
+
except OSError as e:
|
265 |
+
if e.errno == errno.ENOENT:
|
266 |
+
print("Couldn't find program: %r" % cmd[0])
|
267 |
+
return
|
268 |
+
else:
|
269 |
+
raise
|
270 |
+
|
271 |
+
if not cell.endswith('\n'):
|
272 |
+
cell += '\n'
|
273 |
+
cell = cell.encode('utf8', 'replace')
|
274 |
+
if args.bg:
|
275 |
+
self.bg_processes.append(p)
|
276 |
+
self._gc_bg_processes()
|
277 |
+
to_close = []
|
278 |
+
if args.out:
|
279 |
+
self.shell.user_ns[args.out] = _AsyncIOProxy(p.stdout, event_loop)
|
280 |
+
else:
|
281 |
+
to_close.append(p.stdout)
|
282 |
+
if args.err:
|
283 |
+
self.shell.user_ns[args.err] = _AsyncIOProxy(p.stderr, event_loop)
|
284 |
+
else:
|
285 |
+
to_close.append(p.stderr)
|
286 |
+
event_loop.call_soon_threadsafe(
|
287 |
+
lambda: asyncio.Task(self._run_script(p, cell, to_close))
|
288 |
+
)
|
289 |
+
if args.proc:
|
290 |
+
proc_proxy = _AsyncIOProxy(p, event_loop)
|
291 |
+
proc_proxy.stdout = _AsyncIOProxy(p.stdout, event_loop)
|
292 |
+
proc_proxy.stderr = _AsyncIOProxy(p.stderr, event_loop)
|
293 |
+
self.shell.user_ns[args.proc] = proc_proxy
|
294 |
+
return
|
295 |
+
|
296 |
+
try:
|
297 |
+
in_thread(_stream_communicate(p, cell))
|
298 |
+
except KeyboardInterrupt:
|
299 |
+
try:
|
300 |
+
p.send_signal(signal.SIGINT)
|
301 |
+
in_thread(asyncio.wait_for(p.wait(), timeout=0.1))
|
302 |
+
if p.returncode is not None:
|
303 |
+
print("Process was interrupted.")
|
304 |
+
if args.raise_error:
|
305 |
+
raise RaiseAfterInterrupt()
|
306 |
+
else:
|
307 |
+
return
|
308 |
+
p.terminate()
|
309 |
+
in_thread(asyncio.wait_for(p.wait(), timeout=0.1))
|
310 |
+
if p.returncode is not None:
|
311 |
+
print("Process was terminated.")
|
312 |
+
if args.raise_error:
|
313 |
+
raise RaiseAfterInterrupt()
|
314 |
+
else:
|
315 |
+
return
|
316 |
+
p.kill()
|
317 |
+
print("Process was killed.")
|
318 |
+
if args.raise_error:
|
319 |
+
raise RaiseAfterInterrupt()
|
320 |
+
except RaiseAfterInterrupt:
|
321 |
+
pass
|
322 |
+
except OSError:
|
323 |
+
pass
|
324 |
+
except Exception as e:
|
325 |
+
print("Error while terminating subprocess (pid=%i): %s" % (p.pid, e))
|
326 |
+
if args.raise_error:
|
327 |
+
raise CalledProcessError(p.returncode, cell) from None
|
328 |
+
else:
|
329 |
+
return
|
330 |
+
|
331 |
+
if args.raise_error and p.returncode != 0:
|
332 |
+
# If we get here and p.returncode is still None, we must have
|
333 |
+
# killed it but not yet seen its return code. We don't wait for it,
|
334 |
+
# in case it's stuck in uninterruptible sleep. -9 = SIGKILL
|
335 |
+
rc = p.returncode or -9
|
336 |
+
raise CalledProcessError(rc, cell)
|
337 |
+
|
338 |
+
shebang.__skip_doctest__ = os.name != "posix"
|
339 |
+
|
340 |
+
async def _run_script(self, p, cell, to_close):
|
341 |
+
"""callback for running the script in the background"""
|
342 |
+
|
343 |
+
p.stdin.write(cell)
|
344 |
+
await p.stdin.drain()
|
345 |
+
p.stdin.close()
|
346 |
+
await p.stdin.wait_closed()
|
347 |
+
await p.wait()
|
348 |
+
# asyncio read pipes have no close
|
349 |
+
# but we should drain the data anyway
|
350 |
+
for s in to_close:
|
351 |
+
await s.read()
|
352 |
+
self._gc_bg_processes()
|
353 |
+
|
354 |
+
@line_magic("killbgscripts")
|
355 |
+
def killbgscripts(self, _nouse_=''):
|
356 |
+
"""Kill all BG processes started by %%script and its family."""
|
357 |
+
self.kill_bg_processes()
|
358 |
+
print("All background processes were killed.")
|
359 |
+
|
360 |
+
def kill_bg_processes(self):
|
361 |
+
"""Kill all BG processes which are still running."""
|
362 |
+
if not self.bg_processes:
|
363 |
+
return
|
364 |
+
for p in self.bg_processes:
|
365 |
+
if p.returncode is None:
|
366 |
+
try:
|
367 |
+
p.send_signal(signal.SIGINT)
|
368 |
+
except:
|
369 |
+
pass
|
370 |
+
time.sleep(0.1)
|
371 |
+
self._gc_bg_processes()
|
372 |
+
if not self.bg_processes:
|
373 |
+
return
|
374 |
+
for p in self.bg_processes:
|
375 |
+
if p.returncode is None:
|
376 |
+
try:
|
377 |
+
p.terminate()
|
378 |
+
except:
|
379 |
+
pass
|
380 |
+
time.sleep(0.1)
|
381 |
+
self._gc_bg_processes()
|
382 |
+
if not self.bg_processes:
|
383 |
+
return
|
384 |
+
for p in self.bg_processes:
|
385 |
+
if p.returncode is None:
|
386 |
+
try:
|
387 |
+
p.kill()
|
388 |
+
except:
|
389 |
+
pass
|
390 |
+
self._gc_bg_processes()
|
391 |
+
|
392 |
+
def _gc_bg_processes(self):
|
393 |
+
self.bg_processes = [p for p in self.bg_processes if p.returncode is None]
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/__pycache__/__init__.cpython-313.pyc
ADDED
Binary file (204 Bytes). View file
|
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/__pycache__/debugger.cpython-313.pyc
ADDED
Binary file (9.18 kB). View file
|
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/__pycache__/embed.cpython-313.pyc
ADDED
Binary file (17.5 kB). View file
|
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/__pycache__/interactiveshell.cpython-313.pyc
ADDED
Binary file (47.2 kB). View file
|
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/__pycache__/ipapp.cpython-313.pyc
ADDED
Binary file (14.5 kB). View file
|
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/__pycache__/magics.cpython-313.pyc
ADDED
Binary file (9.81 kB). View file
|
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/__pycache__/prompts.cpython-313.pyc
ADDED
Binary file (8.35 kB). View file
|
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/__pycache__/ptutils.cpython-313.pyc
ADDED
Binary file (9.89 kB). View file
|
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/__init__.py
ADDED
@@ -0,0 +1,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import importlib
|
2 |
+
import os
|
3 |
+
from typing import Tuple, Callable
|
4 |
+
|
5 |
+
aliases = {
|
6 |
+
'qt4': 'qt',
|
7 |
+
'gtk2': 'gtk',
|
8 |
+
}
|
9 |
+
|
10 |
+
backends = [
|
11 |
+
"qt",
|
12 |
+
"qt5",
|
13 |
+
"qt6",
|
14 |
+
"gtk",
|
15 |
+
"gtk2",
|
16 |
+
"gtk3",
|
17 |
+
"gtk4",
|
18 |
+
"tk",
|
19 |
+
"wx",
|
20 |
+
"pyglet",
|
21 |
+
"glut",
|
22 |
+
"osx",
|
23 |
+
"asyncio",
|
24 |
+
]
|
25 |
+
|
26 |
+
registered = {}
|
27 |
+
|
28 |
+
def register(name, inputhook):
|
29 |
+
"""Register the function *inputhook* as an event loop integration."""
|
30 |
+
registered[name] = inputhook
|
31 |
+
|
32 |
+
|
33 |
+
class UnknownBackend(KeyError):
|
34 |
+
def __init__(self, name):
|
35 |
+
self.name = name
|
36 |
+
|
37 |
+
def __str__(self):
|
38 |
+
return ("No event loop integration for {!r}. "
|
39 |
+
"Supported event loops are: {}").format(self.name,
|
40 |
+
', '.join(backends + sorted(registered)))
|
41 |
+
|
42 |
+
|
43 |
+
def set_qt_api(gui):
|
44 |
+
"""Sets the `QT_API` environment variable if it isn't already set."""
|
45 |
+
|
46 |
+
qt_api = os.environ.get("QT_API", None)
|
47 |
+
|
48 |
+
from IPython.external.qt_loaders import (
|
49 |
+
QT_API_PYQT,
|
50 |
+
QT_API_PYQT5,
|
51 |
+
QT_API_PYQT6,
|
52 |
+
QT_API_PYSIDE,
|
53 |
+
QT_API_PYSIDE2,
|
54 |
+
QT_API_PYSIDE6,
|
55 |
+
QT_API_PYQTv1,
|
56 |
+
loaded_api,
|
57 |
+
)
|
58 |
+
|
59 |
+
loaded = loaded_api()
|
60 |
+
|
61 |
+
qt_env2gui = {
|
62 |
+
QT_API_PYSIDE: "qt4",
|
63 |
+
QT_API_PYQTv1: "qt4",
|
64 |
+
QT_API_PYQT: "qt4",
|
65 |
+
QT_API_PYSIDE2: "qt5",
|
66 |
+
QT_API_PYQT5: "qt5",
|
67 |
+
QT_API_PYSIDE6: "qt6",
|
68 |
+
QT_API_PYQT6: "qt6",
|
69 |
+
}
|
70 |
+
if loaded is not None and gui != "qt":
|
71 |
+
if qt_env2gui[loaded] != gui:
|
72 |
+
print(
|
73 |
+
f"Cannot switch Qt versions for this session; will use {qt_env2gui[loaded]}."
|
74 |
+
)
|
75 |
+
return qt_env2gui[loaded]
|
76 |
+
|
77 |
+
if qt_api is not None and gui != "qt":
|
78 |
+
if qt_env2gui[qt_api] != gui:
|
79 |
+
print(
|
80 |
+
f'Request for "{gui}" will be ignored because `QT_API` '
|
81 |
+
f'environment variable is set to "{qt_api}"'
|
82 |
+
)
|
83 |
+
return qt_env2gui[qt_api]
|
84 |
+
else:
|
85 |
+
if gui == "qt5":
|
86 |
+
try:
|
87 |
+
import PyQt5 # noqa
|
88 |
+
|
89 |
+
os.environ["QT_API"] = "pyqt5"
|
90 |
+
except ImportError:
|
91 |
+
try:
|
92 |
+
import PySide2 # noqa
|
93 |
+
|
94 |
+
os.environ["QT_API"] = "pyside2"
|
95 |
+
except ImportError:
|
96 |
+
os.environ["QT_API"] = "pyqt5"
|
97 |
+
elif gui == "qt6":
|
98 |
+
try:
|
99 |
+
import PyQt6 # noqa
|
100 |
+
|
101 |
+
os.environ["QT_API"] = "pyqt6"
|
102 |
+
except ImportError:
|
103 |
+
try:
|
104 |
+
import PySide6 # noqa
|
105 |
+
|
106 |
+
os.environ["QT_API"] = "pyside6"
|
107 |
+
except ImportError:
|
108 |
+
os.environ["QT_API"] = "pyqt6"
|
109 |
+
elif gui == "qt":
|
110 |
+
# Don't set QT_API; let IPython logic choose the version.
|
111 |
+
if "QT_API" in os.environ.keys():
|
112 |
+
del os.environ["QT_API"]
|
113 |
+
else:
|
114 |
+
print(f'Unrecognized Qt version: {gui}. Should be "qt5", "qt6", or "qt".')
|
115 |
+
return
|
116 |
+
|
117 |
+
# Import it now so we can figure out which version it is.
|
118 |
+
from IPython.external.qt_for_kernel import QT_API
|
119 |
+
|
120 |
+
return qt_env2gui[QT_API]
|
121 |
+
|
122 |
+
|
123 |
+
def get_inputhook_name_and_func(gui: str) -> Tuple[str, Callable]:
|
124 |
+
if gui in registered:
|
125 |
+
return gui, registered[gui]
|
126 |
+
|
127 |
+
if gui not in backends:
|
128 |
+
raise UnknownBackend(gui)
|
129 |
+
|
130 |
+
if gui in aliases:
|
131 |
+
return get_inputhook_name_and_func(aliases[gui])
|
132 |
+
|
133 |
+
gui_mod = gui
|
134 |
+
if gui.startswith("qt"):
|
135 |
+
gui = set_qt_api(gui)
|
136 |
+
gui_mod = "qt"
|
137 |
+
|
138 |
+
mod = importlib.import_module("IPython.terminal.pt_inputhooks." + gui_mod)
|
139 |
+
return gui, mod.inputhook
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/__pycache__/__init__.cpython-313.pyc
ADDED
Binary file (4.57 kB). View file
|
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/asyncio.py
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Inputhook for running the original asyncio event loop while we're waiting for
|
3 |
+
input.
|
4 |
+
|
5 |
+
By default, in IPython, we run the prompt with a different asyncio event loop,
|
6 |
+
because otherwise we risk that people are freezing the prompt by scheduling bad
|
7 |
+
coroutines. E.g., a coroutine that does a while/true and never yield back
|
8 |
+
control to the loop. We can't cancel that.
|
9 |
+
|
10 |
+
However, sometimes we want the asyncio loop to keep running while waiting for
|
11 |
+
a prompt.
|
12 |
+
|
13 |
+
The following example will print the numbers from 1 to 10 above the prompt,
|
14 |
+
while we are waiting for input. (This works also because we use
|
15 |
+
prompt_toolkit`s `patch_stdout`)::
|
16 |
+
|
17 |
+
In [1]: import asyncio
|
18 |
+
|
19 |
+
In [2]: %gui asyncio
|
20 |
+
|
21 |
+
In [3]: async def f():
|
22 |
+
...: for i in range(10):
|
23 |
+
...: await asyncio.sleep(1)
|
24 |
+
...: print(i)
|
25 |
+
|
26 |
+
|
27 |
+
In [4]: asyncio.ensure_future(f())
|
28 |
+
|
29 |
+
"""
|
30 |
+
|
31 |
+
|
32 |
+
def inputhook(context):
|
33 |
+
"""
|
34 |
+
Inputhook for asyncio event loop integration.
|
35 |
+
"""
|
36 |
+
# For prompt_toolkit 3.0, this input hook literally doesn't do anything.
|
37 |
+
# The event loop integration here is implemented in `interactiveshell.py`
|
38 |
+
# by running the prompt itself in the current asyncio loop. The main reason
|
39 |
+
# for this is that nesting asyncio event loops is unreliable.
|
40 |
+
return
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/glut.py
ADDED
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""GLUT Input hook for interactive use with prompt_toolkit
|
2 |
+
"""
|
3 |
+
|
4 |
+
|
5 |
+
# GLUT is quite an old library and it is difficult to ensure proper
|
6 |
+
# integration within IPython since original GLUT does not allow to handle
|
7 |
+
# events one by one. Instead, it requires for the mainloop to be entered
|
8 |
+
# and never returned (there is not even a function to exit he
|
9 |
+
# mainloop). Fortunately, there are alternatives such as freeglut
|
10 |
+
# (available for linux and windows) and the OSX implementation gives
|
11 |
+
# access to a glutCheckLoop() function that blocks itself until a new
|
12 |
+
# event is received. This means we have to setup the idle callback to
|
13 |
+
# ensure we got at least one event that will unblock the function.
|
14 |
+
#
|
15 |
+
# Furthermore, it is not possible to install these handlers without a window
|
16 |
+
# being first created. We choose to make this window invisible. This means that
|
17 |
+
# display mode options are set at this level and user won't be able to change
|
18 |
+
# them later without modifying the code. This should probably be made available
|
19 |
+
# via IPython options system.
|
20 |
+
|
21 |
+
import sys
|
22 |
+
import time
|
23 |
+
import signal
|
24 |
+
import OpenGL.GLUT as glut
|
25 |
+
import OpenGL.platform as platform
|
26 |
+
from timeit import default_timer as clock
|
27 |
+
|
28 |
+
# Frame per second : 60
|
29 |
+
# Should probably be an IPython option
|
30 |
+
glut_fps = 60
|
31 |
+
|
32 |
+
# Display mode : double buffeed + rgba + depth
|
33 |
+
# Should probably be an IPython option
|
34 |
+
glut_display_mode = (glut.GLUT_DOUBLE |
|
35 |
+
glut.GLUT_RGBA |
|
36 |
+
glut.GLUT_DEPTH)
|
37 |
+
|
38 |
+
glutMainLoopEvent = None
|
39 |
+
if sys.platform == 'darwin':
|
40 |
+
try:
|
41 |
+
glutCheckLoop = platform.createBaseFunction(
|
42 |
+
'glutCheckLoop', dll=platform.GLUT, resultType=None,
|
43 |
+
argTypes=[],
|
44 |
+
doc='glutCheckLoop( ) -> None',
|
45 |
+
argNames=(),
|
46 |
+
)
|
47 |
+
except AttributeError as e:
|
48 |
+
raise RuntimeError(
|
49 |
+
'''Your glut implementation does not allow interactive sessions. '''
|
50 |
+
'''Consider installing freeglut.''') from e
|
51 |
+
glutMainLoopEvent = glutCheckLoop
|
52 |
+
elif glut.HAVE_FREEGLUT:
|
53 |
+
glutMainLoopEvent = glut.glutMainLoopEvent
|
54 |
+
else:
|
55 |
+
raise RuntimeError(
|
56 |
+
'''Your glut implementation does not allow interactive sessions. '''
|
57 |
+
'''Consider installing freeglut.''')
|
58 |
+
|
59 |
+
|
60 |
+
def glut_display():
|
61 |
+
# Dummy display function
|
62 |
+
pass
|
63 |
+
|
64 |
+
def glut_idle():
|
65 |
+
# Dummy idle function
|
66 |
+
pass
|
67 |
+
|
68 |
+
def glut_close():
|
69 |
+
# Close function only hides the current window
|
70 |
+
glut.glutHideWindow()
|
71 |
+
glutMainLoopEvent()
|
72 |
+
|
73 |
+
def glut_int_handler(signum, frame):
|
74 |
+
# Catch sigint and print the defaultipyt message
|
75 |
+
signal.signal(signal.SIGINT, signal.default_int_handler)
|
76 |
+
print('\nKeyboardInterrupt')
|
77 |
+
# Need to reprint the prompt at this stage
|
78 |
+
|
79 |
+
# Initialisation code
|
80 |
+
glut.glutInit( sys.argv )
|
81 |
+
glut.glutInitDisplayMode( glut_display_mode )
|
82 |
+
# This is specific to freeglut
|
83 |
+
if bool(glut.glutSetOption):
|
84 |
+
glut.glutSetOption( glut.GLUT_ACTION_ON_WINDOW_CLOSE,
|
85 |
+
glut.GLUT_ACTION_GLUTMAINLOOP_RETURNS )
|
86 |
+
glut.glutCreateWindow( b'ipython' )
|
87 |
+
glut.glutReshapeWindow( 1, 1 )
|
88 |
+
glut.glutHideWindow( )
|
89 |
+
glut.glutWMCloseFunc( glut_close )
|
90 |
+
glut.glutDisplayFunc( glut_display )
|
91 |
+
glut.glutIdleFunc( glut_idle )
|
92 |
+
|
93 |
+
|
94 |
+
def inputhook(context):
|
95 |
+
"""Run the pyglet event loop by processing pending events only.
|
96 |
+
|
97 |
+
This keeps processing pending events until stdin is ready. After
|
98 |
+
processing all pending events, a call to time.sleep is inserted. This is
|
99 |
+
needed, otherwise, CPU usage is at 100%. This sleep time should be tuned
|
100 |
+
though for best performance.
|
101 |
+
"""
|
102 |
+
# We need to protect against a user pressing Control-C when IPython is
|
103 |
+
# idle and this is running. We trap KeyboardInterrupt and pass.
|
104 |
+
|
105 |
+
signal.signal(signal.SIGINT, glut_int_handler)
|
106 |
+
|
107 |
+
try:
|
108 |
+
t = clock()
|
109 |
+
|
110 |
+
# Make sure the default window is set after a window has been closed
|
111 |
+
if glut.glutGetWindow() == 0:
|
112 |
+
glut.glutSetWindow( 1 )
|
113 |
+
glutMainLoopEvent()
|
114 |
+
return 0
|
115 |
+
|
116 |
+
while not context.input_is_ready():
|
117 |
+
glutMainLoopEvent()
|
118 |
+
# We need to sleep at this point to keep the idle CPU load
|
119 |
+
# low. However, if sleep to long, GUI response is poor. As
|
120 |
+
# a compromise, we watch how often GUI events are being processed
|
121 |
+
# and switch between a short and long sleep time. Here are some
|
122 |
+
# stats useful in helping to tune this.
|
123 |
+
# time CPU load
|
124 |
+
# 0.001 13%
|
125 |
+
# 0.005 3%
|
126 |
+
# 0.01 1.5%
|
127 |
+
# 0.05 0.5%
|
128 |
+
used_time = clock() - t
|
129 |
+
if used_time > 10.0:
|
130 |
+
# print('Sleep for 1 s') # dbg
|
131 |
+
time.sleep(1.0)
|
132 |
+
elif used_time > 0.1:
|
133 |
+
# Few GUI events coming in, so we can sleep longer
|
134 |
+
# print('Sleep for 0.05 s') # dbg
|
135 |
+
time.sleep(0.05)
|
136 |
+
else:
|
137 |
+
# Many GUI events coming in, so sleep only very little
|
138 |
+
time.sleep(0.001)
|
139 |
+
except KeyboardInterrupt:
|
140 |
+
pass
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/gtk.py
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Code borrowed from python-prompt-toolkit examples
|
2 |
+
# https://github.com/jonathanslenders/python-prompt-toolkit/blob/77cdcfbc7f4b4c34a9d2f9a34d422d7152f16209/examples/inputhook.py
|
3 |
+
|
4 |
+
# Copyright (c) 2014, Jonathan Slenders
|
5 |
+
# All rights reserved.
|
6 |
+
#
|
7 |
+
# Redistribution and use in source and binary forms, with or without modification,
|
8 |
+
# are permitted provided that the following conditions are met:
|
9 |
+
#
|
10 |
+
# * Redistributions of source code must retain the above copyright notice, this
|
11 |
+
# list of conditions and the following disclaimer.
|
12 |
+
#
|
13 |
+
# * Redistributions in binary form must reproduce the above copyright notice, this
|
14 |
+
# list of conditions and the following disclaimer in the documentation and/or
|
15 |
+
# other materials provided with the distribution.
|
16 |
+
#
|
17 |
+
# * Neither the name of the {organization} nor the names of its
|
18 |
+
# contributors may be used to endorse or promote products derived from
|
19 |
+
# this software without specific prior written permission.
|
20 |
+
#
|
21 |
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
22 |
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
23 |
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
24 |
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
25 |
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
26 |
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
27 |
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
28 |
+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
29 |
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
30 |
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
31 |
+
|
32 |
+
"""
|
33 |
+
PyGTK input hook for prompt_toolkit.
|
34 |
+
|
35 |
+
Listens on the pipe prompt_toolkit sets up for a notification that it should
|
36 |
+
return control to the terminal event loop.
|
37 |
+
"""
|
38 |
+
|
39 |
+
import gtk, gobject
|
40 |
+
|
41 |
+
# Enable threading in GTK. (Otherwise, GTK will keep the GIL.)
|
42 |
+
gtk.gdk.threads_init()
|
43 |
+
|
44 |
+
|
45 |
+
def inputhook(context):
|
46 |
+
"""
|
47 |
+
When the eventloop of prompt-toolkit is idle, call this inputhook.
|
48 |
+
|
49 |
+
This will run the GTK main loop until the file descriptor
|
50 |
+
`context.fileno()` becomes ready.
|
51 |
+
|
52 |
+
:param context: An `InputHookContext` instance.
|
53 |
+
"""
|
54 |
+
|
55 |
+
def _main_quit(*a, **kw):
|
56 |
+
gtk.main_quit()
|
57 |
+
return False
|
58 |
+
|
59 |
+
gobject.io_add_watch(context.fileno(), gobject.IO_IN, _main_quit)
|
60 |
+
gtk.main()
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/gtk3.py
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""prompt_toolkit input hook for GTK 3"""
|
2 |
+
|
3 |
+
from gi.repository import Gtk, GLib
|
4 |
+
|
5 |
+
|
6 |
+
def _main_quit(*args, **kwargs):
|
7 |
+
Gtk.main_quit()
|
8 |
+
return False
|
9 |
+
|
10 |
+
|
11 |
+
def inputhook(context):
|
12 |
+
GLib.io_add_watch(context.fileno(), GLib.PRIORITY_DEFAULT, GLib.IO_IN, _main_quit)
|
13 |
+
Gtk.main()
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/gtk4.py
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
prompt_toolkit input hook for GTK 4.
|
3 |
+
"""
|
4 |
+
|
5 |
+
from gi.repository import GLib
|
6 |
+
|
7 |
+
|
8 |
+
class _InputHook:
|
9 |
+
def __init__(self, context):
|
10 |
+
self._quit = False
|
11 |
+
GLib.io_add_watch(
|
12 |
+
context.fileno(), GLib.PRIORITY_DEFAULT, GLib.IO_IN, self.quit
|
13 |
+
)
|
14 |
+
|
15 |
+
def quit(self, *args, **kwargs):
|
16 |
+
self._quit = True
|
17 |
+
return False
|
18 |
+
|
19 |
+
def run(self):
|
20 |
+
context = GLib.MainContext.default()
|
21 |
+
while not self._quit:
|
22 |
+
context.iteration(True)
|
23 |
+
|
24 |
+
|
25 |
+
def inputhook(context):
|
26 |
+
hook = _InputHook(context)
|
27 |
+
hook.run()
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/osx.py
ADDED
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Inputhook for OS X
|
2 |
+
|
3 |
+
Calls NSApp / CoreFoundation APIs via ctypes.
|
4 |
+
"""
|
5 |
+
|
6 |
+
# obj-c boilerplate from appnope, used under BSD 2-clause
|
7 |
+
|
8 |
+
import ctypes
|
9 |
+
import ctypes.util
|
10 |
+
from threading import Event
|
11 |
+
|
12 |
+
objc = ctypes.cdll.LoadLibrary(ctypes.util.find_library("objc")) # type: ignore
|
13 |
+
|
14 |
+
void_p = ctypes.c_void_p
|
15 |
+
|
16 |
+
objc.objc_getClass.restype = void_p
|
17 |
+
objc.sel_registerName.restype = void_p
|
18 |
+
objc.objc_msgSend.restype = void_p
|
19 |
+
objc.objc_msgSend.argtypes = [void_p, void_p]
|
20 |
+
|
21 |
+
msg = objc.objc_msgSend
|
22 |
+
|
23 |
+
def _utf8(s):
|
24 |
+
"""ensure utf8 bytes"""
|
25 |
+
if not isinstance(s, bytes):
|
26 |
+
s = s.encode('utf8')
|
27 |
+
return s
|
28 |
+
|
29 |
+
def n(name):
|
30 |
+
"""create a selector name (for ObjC methods)"""
|
31 |
+
return objc.sel_registerName(_utf8(name))
|
32 |
+
|
33 |
+
def C(classname):
|
34 |
+
"""get an ObjC Class by name"""
|
35 |
+
return objc.objc_getClass(_utf8(classname))
|
36 |
+
|
37 |
+
# end obj-c boilerplate from appnope
|
38 |
+
|
39 |
+
# CoreFoundation C-API calls we will use:
|
40 |
+
CoreFoundation = ctypes.cdll.LoadLibrary(ctypes.util.find_library("CoreFoundation")) # type: ignore
|
41 |
+
|
42 |
+
CFFileDescriptorCreate = CoreFoundation.CFFileDescriptorCreate
|
43 |
+
CFFileDescriptorCreate.restype = void_p
|
44 |
+
CFFileDescriptorCreate.argtypes = [void_p, ctypes.c_int, ctypes.c_bool, void_p, void_p]
|
45 |
+
|
46 |
+
CFFileDescriptorGetNativeDescriptor = CoreFoundation.CFFileDescriptorGetNativeDescriptor
|
47 |
+
CFFileDescriptorGetNativeDescriptor.restype = ctypes.c_int
|
48 |
+
CFFileDescriptorGetNativeDescriptor.argtypes = [void_p]
|
49 |
+
|
50 |
+
CFFileDescriptorEnableCallBacks = CoreFoundation.CFFileDescriptorEnableCallBacks
|
51 |
+
CFFileDescriptorEnableCallBacks.restype = None
|
52 |
+
CFFileDescriptorEnableCallBacks.argtypes = [void_p, ctypes.c_ulong]
|
53 |
+
|
54 |
+
CFFileDescriptorCreateRunLoopSource = CoreFoundation.CFFileDescriptorCreateRunLoopSource
|
55 |
+
CFFileDescriptorCreateRunLoopSource.restype = void_p
|
56 |
+
CFFileDescriptorCreateRunLoopSource.argtypes = [void_p, void_p, void_p]
|
57 |
+
|
58 |
+
CFRunLoopGetCurrent = CoreFoundation.CFRunLoopGetCurrent
|
59 |
+
CFRunLoopGetCurrent.restype = void_p
|
60 |
+
|
61 |
+
CFRunLoopAddSource = CoreFoundation.CFRunLoopAddSource
|
62 |
+
CFRunLoopAddSource.restype = None
|
63 |
+
CFRunLoopAddSource.argtypes = [void_p, void_p, void_p]
|
64 |
+
|
65 |
+
CFRelease = CoreFoundation.CFRelease
|
66 |
+
CFRelease.restype = None
|
67 |
+
CFRelease.argtypes = [void_p]
|
68 |
+
|
69 |
+
CFFileDescriptorInvalidate = CoreFoundation.CFFileDescriptorInvalidate
|
70 |
+
CFFileDescriptorInvalidate.restype = None
|
71 |
+
CFFileDescriptorInvalidate.argtypes = [void_p]
|
72 |
+
|
73 |
+
# From CFFileDescriptor.h
|
74 |
+
kCFFileDescriptorReadCallBack = 1
|
75 |
+
kCFRunLoopCommonModes = void_p.in_dll(CoreFoundation, 'kCFRunLoopCommonModes')
|
76 |
+
|
77 |
+
|
78 |
+
def _NSApp():
|
79 |
+
"""Return the global NSApplication instance (NSApp)"""
|
80 |
+
objc.objc_msgSend.argtypes = [void_p, void_p]
|
81 |
+
return msg(C('NSApplication'), n('sharedApplication'))
|
82 |
+
|
83 |
+
|
84 |
+
def _wake(NSApp):
|
85 |
+
"""Wake the Application"""
|
86 |
+
objc.objc_msgSend.argtypes = [
|
87 |
+
void_p,
|
88 |
+
void_p,
|
89 |
+
void_p,
|
90 |
+
void_p,
|
91 |
+
void_p,
|
92 |
+
void_p,
|
93 |
+
void_p,
|
94 |
+
void_p,
|
95 |
+
void_p,
|
96 |
+
void_p,
|
97 |
+
void_p,
|
98 |
+
]
|
99 |
+
event = msg(
|
100 |
+
C("NSEvent"),
|
101 |
+
n(
|
102 |
+
"otherEventWithType:location:modifierFlags:"
|
103 |
+
"timestamp:windowNumber:context:subtype:data1:data2:"
|
104 |
+
),
|
105 |
+
15, # Type
|
106 |
+
0, # location
|
107 |
+
0, # flags
|
108 |
+
0, # timestamp
|
109 |
+
0, # window
|
110 |
+
None, # context
|
111 |
+
0, # subtype
|
112 |
+
0, # data1
|
113 |
+
0, # data2
|
114 |
+
)
|
115 |
+
objc.objc_msgSend.argtypes = [void_p, void_p, void_p, void_p]
|
116 |
+
msg(NSApp, n('postEvent:atStart:'), void_p(event), True)
|
117 |
+
|
118 |
+
|
119 |
+
def _input_callback(fdref, flags, info):
|
120 |
+
"""Callback to fire when there's input to be read"""
|
121 |
+
CFFileDescriptorInvalidate(fdref)
|
122 |
+
CFRelease(fdref)
|
123 |
+
NSApp = _NSApp()
|
124 |
+
objc.objc_msgSend.argtypes = [void_p, void_p, void_p]
|
125 |
+
msg(NSApp, n('stop:'), NSApp)
|
126 |
+
_wake(NSApp)
|
127 |
+
|
128 |
+
_c_callback_func_type = ctypes.CFUNCTYPE(None, void_p, void_p, void_p)
|
129 |
+
_c_input_callback = _c_callback_func_type(_input_callback)
|
130 |
+
|
131 |
+
|
132 |
+
def _stop_on_read(fd):
|
133 |
+
"""Register callback to stop eventloop when there's data on fd"""
|
134 |
+
fdref = CFFileDescriptorCreate(None, fd, False, _c_input_callback, None)
|
135 |
+
CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack)
|
136 |
+
source = CFFileDescriptorCreateRunLoopSource(None, fdref, 0)
|
137 |
+
loop = CFRunLoopGetCurrent()
|
138 |
+
CFRunLoopAddSource(loop, source, kCFRunLoopCommonModes)
|
139 |
+
CFRelease(source)
|
140 |
+
|
141 |
+
|
142 |
+
def inputhook(context):
|
143 |
+
"""Inputhook for Cocoa (NSApp)"""
|
144 |
+
NSApp = _NSApp()
|
145 |
+
_stop_on_read(context.fileno())
|
146 |
+
objc.objc_msgSend.argtypes = [void_p, void_p]
|
147 |
+
msg(NSApp, n('run'))
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/pyglet.py
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Enable pyglet to be used interactively with prompt_toolkit"""
|
2 |
+
|
3 |
+
import sys
|
4 |
+
import time
|
5 |
+
from timeit import default_timer as clock
|
6 |
+
import pyglet
|
7 |
+
|
8 |
+
# On linux only, window.flip() has a bug that causes an AttributeError on
|
9 |
+
# window close. For details, see:
|
10 |
+
# http://groups.google.com/group/pyglet-users/browse_thread/thread/47c1aab9aa4a3d23/c22f9e819826799e?#c22f9e819826799e
|
11 |
+
|
12 |
+
if sys.platform.startswith("linux"):
|
13 |
+
|
14 |
+
def flip(window):
|
15 |
+
try:
|
16 |
+
window.flip()
|
17 |
+
except AttributeError:
|
18 |
+
pass
|
19 |
+
else:
|
20 |
+
|
21 |
+
def flip(window):
|
22 |
+
window.flip()
|
23 |
+
|
24 |
+
|
25 |
+
def inputhook(context):
|
26 |
+
"""Run the pyglet event loop by processing pending events only.
|
27 |
+
|
28 |
+
This keeps processing pending events until stdin is ready. After
|
29 |
+
processing all pending events, a call to time.sleep is inserted. This is
|
30 |
+
needed, otherwise, CPU usage is at 100%. This sleep time should be tuned
|
31 |
+
though for best performance.
|
32 |
+
"""
|
33 |
+
# We need to protect against a user pressing Control-C when IPython is
|
34 |
+
# idle and this is running. We trap KeyboardInterrupt and pass.
|
35 |
+
try:
|
36 |
+
t = clock()
|
37 |
+
while not context.input_is_ready():
|
38 |
+
pyglet.clock.tick()
|
39 |
+
for window in pyglet.app.windows:
|
40 |
+
window.switch_to()
|
41 |
+
window.dispatch_events()
|
42 |
+
window.dispatch_event("on_draw")
|
43 |
+
flip(window)
|
44 |
+
|
45 |
+
# We need to sleep at this point to keep the idle CPU load
|
46 |
+
# low. However, if sleep to long, GUI response is poor. As
|
47 |
+
# a compromise, we watch how often GUI events are being processed
|
48 |
+
# and switch between a short and long sleep time. Here are some
|
49 |
+
# stats useful in helping to tune this.
|
50 |
+
# time CPU load
|
51 |
+
# 0.001 13%
|
52 |
+
# 0.005 3%
|
53 |
+
# 0.01 1.5%
|
54 |
+
# 0.05 0.5%
|
55 |
+
used_time = clock() - t
|
56 |
+
if used_time > 10.0:
|
57 |
+
# print('Sleep for 1 s') # dbg
|
58 |
+
time.sleep(1.0)
|
59 |
+
elif used_time > 0.1:
|
60 |
+
# Few GUI events coming in, so we can sleep longer
|
61 |
+
# print('Sleep for 0.05 s') # dbg
|
62 |
+
time.sleep(0.05)
|
63 |
+
else:
|
64 |
+
# Many GUI events coming in, so sleep only very little
|
65 |
+
time.sleep(0.001)
|
66 |
+
except KeyboardInterrupt:
|
67 |
+
pass
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/qt.py
ADDED
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import sys
|
2 |
+
import os
|
3 |
+
from IPython.external.qt_for_kernel import QtCore, QtGui, enum_helper
|
4 |
+
from IPython import get_ipython
|
5 |
+
|
6 |
+
# If we create a QApplication, QEventLoop, or a QTimer, keep a reference to them
|
7 |
+
# so that they don't get garbage collected or leak memory when created multiple times.
|
8 |
+
_appref = None
|
9 |
+
_eventloop = None
|
10 |
+
_timer = None
|
11 |
+
_already_warned = False
|
12 |
+
|
13 |
+
|
14 |
+
def _exec(obj):
|
15 |
+
# exec on PyQt6, exec_ elsewhere.
|
16 |
+
obj.exec() if hasattr(obj, "exec") else obj.exec_()
|
17 |
+
|
18 |
+
|
19 |
+
def _reclaim_excepthook():
|
20 |
+
shell = get_ipython()
|
21 |
+
if shell is not None:
|
22 |
+
sys.excepthook = shell.excepthook
|
23 |
+
|
24 |
+
|
25 |
+
def inputhook(context):
|
26 |
+
global _appref, _eventloop, _timer
|
27 |
+
app = QtCore.QCoreApplication.instance()
|
28 |
+
if not app:
|
29 |
+
if sys.platform == 'linux':
|
30 |
+
if not os.environ.get('DISPLAY') \
|
31 |
+
and not os.environ.get('WAYLAND_DISPLAY'):
|
32 |
+
import warnings
|
33 |
+
global _already_warned
|
34 |
+
if not _already_warned:
|
35 |
+
_already_warned = True
|
36 |
+
warnings.warn(
|
37 |
+
'The DISPLAY or WAYLAND_DISPLAY environment variable is '
|
38 |
+
'not set or empty and Qt5 requires this environment '
|
39 |
+
'variable. Deactivate Qt5 code.'
|
40 |
+
)
|
41 |
+
return
|
42 |
+
try:
|
43 |
+
QtCore.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
|
44 |
+
except AttributeError: # Only for Qt>=5.6, <6.
|
45 |
+
pass
|
46 |
+
try:
|
47 |
+
QtCore.QApplication.setHighDpiScaleFactorRoundingPolicy(
|
48 |
+
QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough
|
49 |
+
)
|
50 |
+
except AttributeError: # Only for Qt>=5.14.
|
51 |
+
pass
|
52 |
+
_appref = app = QtGui.QApplication([" "])
|
53 |
+
|
54 |
+
# "reclaim" IPython sys.excepthook after event loop starts
|
55 |
+
# without this, it defaults back to BaseIPythonApplication.excepthook
|
56 |
+
# and exceptions in the Qt event loop are rendered without traceback
|
57 |
+
# formatting and look like "bug in IPython".
|
58 |
+
QtCore.QTimer.singleShot(0, _reclaim_excepthook)
|
59 |
+
|
60 |
+
if _eventloop is None:
|
61 |
+
_eventloop = QtCore.QEventLoop(app)
|
62 |
+
|
63 |
+
if sys.platform == 'win32':
|
64 |
+
# The QSocketNotifier method doesn't appear to work on Windows.
|
65 |
+
# Use polling instead.
|
66 |
+
if _timer is None:
|
67 |
+
_timer = QtCore.QTimer()
|
68 |
+
_timer.timeout.connect(_eventloop.quit)
|
69 |
+
while not context.input_is_ready():
|
70 |
+
# NOTE: run the event loop, and after 10 ms, call `quit` to exit it.
|
71 |
+
_timer.start(10) # 10 ms
|
72 |
+
_exec(_eventloop)
|
73 |
+
_timer.stop()
|
74 |
+
else:
|
75 |
+
# On POSIX platforms, we can use a file descriptor to quit the event
|
76 |
+
# loop when there is input ready to read.
|
77 |
+
notifier = QtCore.QSocketNotifier(
|
78 |
+
context.fileno(), enum_helper("QtCore.QSocketNotifier.Type").Read
|
79 |
+
)
|
80 |
+
try:
|
81 |
+
# connect the callback we care about before we turn it on
|
82 |
+
# lambda is necessary as PyQT inspect the function signature to know
|
83 |
+
# what arguments to pass to. See https://github.com/ipython/ipython/pull/12355
|
84 |
+
notifier.activated.connect(lambda: _eventloop.exit())
|
85 |
+
notifier.setEnabled(True)
|
86 |
+
# only start the event loop we are not already flipped
|
87 |
+
if not context.input_is_ready():
|
88 |
+
_exec(_eventloop)
|
89 |
+
finally:
|
90 |
+
notifier.setEnabled(False)
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/tk.py
ADDED
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Code borrowed from ptpython
|
2 |
+
# https://github.com/jonathanslenders/ptpython/blob/86b71a89626114b18898a0af463978bdb32eeb70/ptpython/eventloop.py
|
3 |
+
|
4 |
+
# Copyright (c) 2015, Jonathan Slenders
|
5 |
+
# All rights reserved.
|
6 |
+
#
|
7 |
+
# Redistribution and use in source and binary forms, with or without modification,
|
8 |
+
# are permitted provided that the following conditions are met:
|
9 |
+
#
|
10 |
+
# * Redistributions of source code must retain the above copyright notice, this
|
11 |
+
# list of conditions and the following disclaimer.
|
12 |
+
#
|
13 |
+
# * Redistributions in binary form must reproduce the above copyright notice, this
|
14 |
+
# list of conditions and the following disclaimer in the documentation and/or
|
15 |
+
# other materials provided with the distribution.
|
16 |
+
#
|
17 |
+
# * Neither the name of the {organization} nor the names of its
|
18 |
+
# contributors may be used to endorse or promote products derived from
|
19 |
+
# this software without specific prior written permission.
|
20 |
+
#
|
21 |
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
22 |
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
23 |
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
24 |
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
25 |
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
26 |
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
27 |
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
28 |
+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
29 |
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
30 |
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
31 |
+
|
32 |
+
"""
|
33 |
+
Wrapper around the eventloop that gives some time to the Tkinter GUI to process
|
34 |
+
events when it's loaded and while we are waiting for input at the REPL. This
|
35 |
+
way we don't block the UI of for instance ``turtle`` and other Tk libraries.
|
36 |
+
|
37 |
+
(Normally Tkinter registers it's callbacks in ``PyOS_InputHook`` to integrate
|
38 |
+
in readline. ``prompt-toolkit`` doesn't understand that input hook, but this
|
39 |
+
will fix it for Tk.)
|
40 |
+
"""
|
41 |
+
|
42 |
+
import time
|
43 |
+
|
44 |
+
import _tkinter
|
45 |
+
import tkinter
|
46 |
+
|
47 |
+
|
48 |
+
def inputhook(inputhook_context):
|
49 |
+
"""
|
50 |
+
Inputhook for Tk.
|
51 |
+
Run the Tk eventloop until prompt-toolkit needs to process the next input.
|
52 |
+
"""
|
53 |
+
# Get the current TK application.
|
54 |
+
root = tkinter._default_root
|
55 |
+
|
56 |
+
def wait_using_filehandler():
|
57 |
+
"""
|
58 |
+
Run the TK eventloop until the file handler that we got from the
|
59 |
+
inputhook becomes readable.
|
60 |
+
"""
|
61 |
+
# Add a handler that sets the stop flag when `prompt-toolkit` has input
|
62 |
+
# to process.
|
63 |
+
stop = [False]
|
64 |
+
|
65 |
+
def done(*a):
|
66 |
+
stop[0] = True
|
67 |
+
|
68 |
+
root.createfilehandler(inputhook_context.fileno(), _tkinter.READABLE, done)
|
69 |
+
|
70 |
+
# Run the TK event loop as long as we don't receive input.
|
71 |
+
while root.dooneevent(_tkinter.ALL_EVENTS):
|
72 |
+
if stop[0]:
|
73 |
+
break
|
74 |
+
|
75 |
+
root.deletefilehandler(inputhook_context.fileno())
|
76 |
+
|
77 |
+
def wait_using_polling():
|
78 |
+
"""
|
79 |
+
Windows TK doesn't support 'createfilehandler'.
|
80 |
+
So, run the TK eventloop and poll until input is ready.
|
81 |
+
"""
|
82 |
+
while not inputhook_context.input_is_ready():
|
83 |
+
while root.dooneevent(_tkinter.ALL_EVENTS | _tkinter.DONT_WAIT):
|
84 |
+
pass
|
85 |
+
# Sleep to make the CPU idle, but not too long, so that the UI
|
86 |
+
# stays responsive.
|
87 |
+
time.sleep(0.01)
|
88 |
+
|
89 |
+
if root is not None:
|
90 |
+
if hasattr(root, "createfilehandler"):
|
91 |
+
wait_using_filehandler()
|
92 |
+
else:
|
93 |
+
wait_using_polling()
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/pt_inputhooks/wx.py
ADDED
@@ -0,0 +1,219 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Enable wxPython to be used interactively in prompt_toolkit
|
2 |
+
"""
|
3 |
+
|
4 |
+
import sys
|
5 |
+
import signal
|
6 |
+
import time
|
7 |
+
from timeit import default_timer as clock
|
8 |
+
import wx
|
9 |
+
|
10 |
+
|
11 |
+
def ignore_keyboardinterrupts(func):
|
12 |
+
"""Decorator which causes KeyboardInterrupt exceptions to be ignored during
|
13 |
+
execution of the decorated function.
|
14 |
+
|
15 |
+
This is used by the inputhook functions to handle the event where the user
|
16 |
+
presses CTRL+C while IPython is idle, and the inputhook loop is running. In
|
17 |
+
this case, we want to ignore interrupts.
|
18 |
+
"""
|
19 |
+
def wrapper(*args, **kwargs):
|
20 |
+
try:
|
21 |
+
func(*args, **kwargs)
|
22 |
+
except KeyboardInterrupt:
|
23 |
+
pass
|
24 |
+
return wrapper
|
25 |
+
|
26 |
+
|
27 |
+
@ignore_keyboardinterrupts
|
28 |
+
def inputhook_wx1(context):
|
29 |
+
"""Run the wx event loop by processing pending events only.
|
30 |
+
|
31 |
+
This approach seems to work, but its performance is not great as it
|
32 |
+
relies on having PyOS_InputHook called regularly.
|
33 |
+
"""
|
34 |
+
app = wx.GetApp()
|
35 |
+
if app is not None:
|
36 |
+
assert wx.Thread_IsMain()
|
37 |
+
|
38 |
+
# Make a temporary event loop and process system events until
|
39 |
+
# there are no more waiting, then allow idle events (which
|
40 |
+
# will also deal with pending or posted wx events.)
|
41 |
+
evtloop = wx.EventLoop()
|
42 |
+
ea = wx.EventLoopActivator(evtloop)
|
43 |
+
while evtloop.Pending():
|
44 |
+
evtloop.Dispatch()
|
45 |
+
app.ProcessIdle()
|
46 |
+
del ea
|
47 |
+
return 0
|
48 |
+
|
49 |
+
|
50 |
+
class EventLoopTimer(wx.Timer):
|
51 |
+
|
52 |
+
def __init__(self, func):
|
53 |
+
self.func = func
|
54 |
+
wx.Timer.__init__(self)
|
55 |
+
|
56 |
+
def Notify(self):
|
57 |
+
self.func()
|
58 |
+
|
59 |
+
|
60 |
+
class EventLoopRunner:
|
61 |
+
|
62 |
+
def Run(self, time, input_is_ready):
|
63 |
+
self.input_is_ready = input_is_ready
|
64 |
+
self.evtloop = wx.EventLoop()
|
65 |
+
self.timer = EventLoopTimer(self.check_stdin)
|
66 |
+
self.timer.Start(time)
|
67 |
+
self.evtloop.Run()
|
68 |
+
|
69 |
+
def check_stdin(self):
|
70 |
+
if self.input_is_ready():
|
71 |
+
self.timer.Stop()
|
72 |
+
self.evtloop.Exit()
|
73 |
+
|
74 |
+
|
75 |
+
@ignore_keyboardinterrupts
|
76 |
+
def inputhook_wx2(context):
|
77 |
+
"""Run the wx event loop, polling for stdin.
|
78 |
+
|
79 |
+
This version runs the wx eventloop for an undetermined amount of time,
|
80 |
+
during which it periodically checks to see if anything is ready on
|
81 |
+
stdin. If anything is ready on stdin, the event loop exits.
|
82 |
+
|
83 |
+
The argument to elr.Run controls how often the event loop looks at stdin.
|
84 |
+
This determines the responsiveness at the keyboard. A setting of 1000
|
85 |
+
enables a user to type at most 1 char per second. I have found that a
|
86 |
+
setting of 10 gives good keyboard response. We can shorten it further,
|
87 |
+
but eventually performance would suffer from calling select/kbhit too
|
88 |
+
often.
|
89 |
+
"""
|
90 |
+
app = wx.GetApp()
|
91 |
+
if app is not None:
|
92 |
+
assert wx.Thread_IsMain()
|
93 |
+
elr = EventLoopRunner()
|
94 |
+
# As this time is made shorter, keyboard response improves, but idle
|
95 |
+
# CPU load goes up. 10 ms seems like a good compromise.
|
96 |
+
elr.Run(time=10, # CHANGE time here to control polling interval
|
97 |
+
input_is_ready=context.input_is_ready)
|
98 |
+
return 0
|
99 |
+
|
100 |
+
|
101 |
+
@ignore_keyboardinterrupts
|
102 |
+
def inputhook_wx3(context):
|
103 |
+
"""Run the wx event loop by processing pending events only.
|
104 |
+
|
105 |
+
This is like inputhook_wx1, but it keeps processing pending events
|
106 |
+
until stdin is ready. After processing all pending events, a call to
|
107 |
+
time.sleep is inserted. This is needed, otherwise, CPU usage is at 100%.
|
108 |
+
This sleep time should be tuned though for best performance.
|
109 |
+
"""
|
110 |
+
app = wx.GetApp()
|
111 |
+
if app is not None:
|
112 |
+
assert wx.Thread_IsMain()
|
113 |
+
|
114 |
+
# The import of wx on Linux sets the handler for signal.SIGINT
|
115 |
+
# to 0. This is a bug in wx or gtk. We fix by just setting it
|
116 |
+
# back to the Python default.
|
117 |
+
if not callable(signal.getsignal(signal.SIGINT)):
|
118 |
+
signal.signal(signal.SIGINT, signal.default_int_handler)
|
119 |
+
|
120 |
+
evtloop = wx.EventLoop()
|
121 |
+
ea = wx.EventLoopActivator(evtloop)
|
122 |
+
t = clock()
|
123 |
+
while not context.input_is_ready():
|
124 |
+
while evtloop.Pending():
|
125 |
+
t = clock()
|
126 |
+
evtloop.Dispatch()
|
127 |
+
app.ProcessIdle()
|
128 |
+
# We need to sleep at this point to keep the idle CPU load
|
129 |
+
# low. However, if sleep to long, GUI response is poor. As
|
130 |
+
# a compromise, we watch how often GUI events are being processed
|
131 |
+
# and switch between a short and long sleep time. Here are some
|
132 |
+
# stats useful in helping to tune this.
|
133 |
+
# time CPU load
|
134 |
+
# 0.001 13%
|
135 |
+
# 0.005 3%
|
136 |
+
# 0.01 1.5%
|
137 |
+
# 0.05 0.5%
|
138 |
+
used_time = clock() - t
|
139 |
+
if used_time > 10.0:
|
140 |
+
# print('Sleep for 1 s') # dbg
|
141 |
+
time.sleep(1.0)
|
142 |
+
elif used_time > 0.1:
|
143 |
+
# Few GUI events coming in, so we can sleep longer
|
144 |
+
# print('Sleep for 0.05 s') # dbg
|
145 |
+
time.sleep(0.05)
|
146 |
+
else:
|
147 |
+
# Many GUI events coming in, so sleep only very little
|
148 |
+
time.sleep(0.001)
|
149 |
+
del ea
|
150 |
+
return 0
|
151 |
+
|
152 |
+
|
153 |
+
@ignore_keyboardinterrupts
|
154 |
+
def inputhook_wxphoenix(context):
|
155 |
+
"""Run the wx event loop until the user provides more input.
|
156 |
+
|
157 |
+
This input hook is suitable for use with wxPython >= 4 (a.k.a. Phoenix).
|
158 |
+
|
159 |
+
It uses the same approach to that used in
|
160 |
+
ipykernel.eventloops.loop_wx. The wx.MainLoop is executed, and a wx.Timer
|
161 |
+
is used to periodically poll the context for input. As soon as input is
|
162 |
+
ready, the wx.MainLoop is stopped.
|
163 |
+
"""
|
164 |
+
|
165 |
+
app = wx.GetApp()
|
166 |
+
|
167 |
+
if app is None:
|
168 |
+
return
|
169 |
+
|
170 |
+
if context.input_is_ready():
|
171 |
+
return
|
172 |
+
|
173 |
+
assert wx.IsMainThread()
|
174 |
+
|
175 |
+
# Wx uses milliseconds
|
176 |
+
poll_interval = 100
|
177 |
+
|
178 |
+
# Use a wx.Timer to periodically check whether input is ready - as soon as
|
179 |
+
# it is, we exit the main loop
|
180 |
+
timer = wx.Timer()
|
181 |
+
|
182 |
+
def poll(ev):
|
183 |
+
if context.input_is_ready():
|
184 |
+
timer.Stop()
|
185 |
+
app.ExitMainLoop()
|
186 |
+
|
187 |
+
timer.Start(poll_interval)
|
188 |
+
timer.Bind(wx.EVT_TIMER, poll)
|
189 |
+
|
190 |
+
# The import of wx on Linux sets the handler for signal.SIGINT to 0. This
|
191 |
+
# is a bug in wx or gtk. We fix by just setting it back to the Python
|
192 |
+
# default.
|
193 |
+
if not callable(signal.getsignal(signal.SIGINT)):
|
194 |
+
signal.signal(signal.SIGINT, signal.default_int_handler)
|
195 |
+
|
196 |
+
# The SetExitOnFrameDelete call allows us to run the wx mainloop without
|
197 |
+
# having a frame open.
|
198 |
+
app.SetExitOnFrameDelete(False)
|
199 |
+
app.MainLoop()
|
200 |
+
|
201 |
+
|
202 |
+
# Get the major wx version number to figure out what input hook we should use.
|
203 |
+
major_version = 3
|
204 |
+
|
205 |
+
try:
|
206 |
+
major_version = int(wx.__version__[0])
|
207 |
+
except Exception:
|
208 |
+
pass
|
209 |
+
|
210 |
+
# Use the phoenix hook on all platforms for wxpython >= 4
|
211 |
+
if major_version >= 4:
|
212 |
+
inputhook = inputhook_wxphoenix
|
213 |
+
# On OSX, evtloop.Pending() always returns True, regardless of there being
|
214 |
+
# any events pending. As such we can't use implementations 1 or 3 of the
|
215 |
+
# inputhook as those depend on a pending/dispatch loop.
|
216 |
+
elif sys.platform == 'darwin':
|
217 |
+
inputhook = inputhook_wx2
|
218 |
+
else:
|
219 |
+
inputhook = inputhook_wx3
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/shortcuts/__init__.py
ADDED
@@ -0,0 +1,636 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Module to define and register Terminal IPython shortcuts with
|
3 |
+
:mod:`prompt_toolkit`
|
4 |
+
"""
|
5 |
+
|
6 |
+
# Copyright (c) IPython Development Team.
|
7 |
+
# Distributed under the terms of the Modified BSD License.
|
8 |
+
|
9 |
+
import os
|
10 |
+
import signal
|
11 |
+
import sys
|
12 |
+
import warnings
|
13 |
+
from dataclasses import dataclass
|
14 |
+
from typing import Callable, Any, Optional, List
|
15 |
+
|
16 |
+
from prompt_toolkit.application.current import get_app
|
17 |
+
from prompt_toolkit.key_binding import KeyBindings
|
18 |
+
from prompt_toolkit.key_binding.key_processor import KeyPressEvent
|
19 |
+
from prompt_toolkit.key_binding.bindings import named_commands as nc
|
20 |
+
from prompt_toolkit.key_binding.bindings.completion import (
|
21 |
+
display_completions_like_readline,
|
22 |
+
)
|
23 |
+
from prompt_toolkit.key_binding.vi_state import InputMode, ViState
|
24 |
+
from prompt_toolkit.filters import Condition
|
25 |
+
|
26 |
+
from IPython.core.getipython import get_ipython
|
27 |
+
from . import auto_match as match
|
28 |
+
from . import auto_suggest
|
29 |
+
from .filters import filter_from_string
|
30 |
+
from IPython.utils.decorators import undoc
|
31 |
+
|
32 |
+
from prompt_toolkit.enums import DEFAULT_BUFFER
|
33 |
+
|
34 |
+
__all__ = ["create_ipython_shortcuts"]
|
35 |
+
|
36 |
+
|
37 |
+
@dataclass
|
38 |
+
class BaseBinding:
|
39 |
+
command: Callable[[KeyPressEvent], Any]
|
40 |
+
keys: List[str]
|
41 |
+
|
42 |
+
|
43 |
+
@dataclass
|
44 |
+
class RuntimeBinding(BaseBinding):
|
45 |
+
filter: Condition
|
46 |
+
|
47 |
+
|
48 |
+
@dataclass
|
49 |
+
class Binding(BaseBinding):
|
50 |
+
# while filter could be created by referencing variables directly (rather
|
51 |
+
# than created from strings), by using strings we ensure that users will
|
52 |
+
# be able to create filters in configuration (e.g. JSON) files too, which
|
53 |
+
# also benefits the documentation by enforcing human-readable filter names.
|
54 |
+
condition: Optional[str] = None
|
55 |
+
|
56 |
+
def __post_init__(self):
|
57 |
+
if self.condition:
|
58 |
+
self.filter = filter_from_string(self.condition)
|
59 |
+
else:
|
60 |
+
self.filter = None
|
61 |
+
|
62 |
+
|
63 |
+
def create_identifier(handler: Callable):
|
64 |
+
parts = handler.__module__.split(".")
|
65 |
+
name = handler.__name__
|
66 |
+
package = parts[0]
|
67 |
+
if len(parts) > 1:
|
68 |
+
final_module = parts[-1]
|
69 |
+
return f"{package}:{final_module}.{name}"
|
70 |
+
else:
|
71 |
+
return f"{package}:{name}"
|
72 |
+
|
73 |
+
|
74 |
+
AUTO_MATCH_BINDINGS = [
|
75 |
+
*[
|
76 |
+
Binding(
|
77 |
+
cmd, [key], "focused_insert & auto_match & followed_by_closing_paren_or_end"
|
78 |
+
)
|
79 |
+
for key, cmd in match.auto_match_parens.items()
|
80 |
+
],
|
81 |
+
*[
|
82 |
+
# raw string
|
83 |
+
Binding(cmd, [key], "focused_insert & auto_match & preceded_by_raw_str_prefix")
|
84 |
+
for key, cmd in match.auto_match_parens_raw_string.items()
|
85 |
+
],
|
86 |
+
Binding(
|
87 |
+
match.double_quote,
|
88 |
+
['"'],
|
89 |
+
"focused_insert"
|
90 |
+
" & auto_match"
|
91 |
+
" & not_inside_unclosed_string"
|
92 |
+
" & preceded_by_paired_double_quotes"
|
93 |
+
" & followed_by_closing_paren_or_end",
|
94 |
+
),
|
95 |
+
Binding(
|
96 |
+
match.single_quote,
|
97 |
+
["'"],
|
98 |
+
"focused_insert"
|
99 |
+
" & auto_match"
|
100 |
+
" & not_inside_unclosed_string"
|
101 |
+
" & preceded_by_paired_single_quotes"
|
102 |
+
" & followed_by_closing_paren_or_end",
|
103 |
+
),
|
104 |
+
Binding(
|
105 |
+
match.docstring_double_quotes,
|
106 |
+
['"'],
|
107 |
+
"focused_insert"
|
108 |
+
" & auto_match"
|
109 |
+
" & not_inside_unclosed_string"
|
110 |
+
" & preceded_by_two_double_quotes",
|
111 |
+
),
|
112 |
+
Binding(
|
113 |
+
match.docstring_single_quotes,
|
114 |
+
["'"],
|
115 |
+
"focused_insert"
|
116 |
+
" & auto_match"
|
117 |
+
" & not_inside_unclosed_string"
|
118 |
+
" & preceded_by_two_single_quotes",
|
119 |
+
),
|
120 |
+
Binding(
|
121 |
+
match.skip_over,
|
122 |
+
[")"],
|
123 |
+
"focused_insert & auto_match & followed_by_closing_round_paren",
|
124 |
+
),
|
125 |
+
Binding(
|
126 |
+
match.skip_over,
|
127 |
+
["]"],
|
128 |
+
"focused_insert & auto_match & followed_by_closing_bracket",
|
129 |
+
),
|
130 |
+
Binding(
|
131 |
+
match.skip_over,
|
132 |
+
["}"],
|
133 |
+
"focused_insert & auto_match & followed_by_closing_brace",
|
134 |
+
),
|
135 |
+
Binding(
|
136 |
+
match.skip_over, ['"'], "focused_insert & auto_match & followed_by_double_quote"
|
137 |
+
),
|
138 |
+
Binding(
|
139 |
+
match.skip_over, ["'"], "focused_insert & auto_match & followed_by_single_quote"
|
140 |
+
),
|
141 |
+
Binding(
|
142 |
+
match.delete_pair,
|
143 |
+
["backspace"],
|
144 |
+
"focused_insert"
|
145 |
+
" & preceded_by_opening_round_paren"
|
146 |
+
" & auto_match"
|
147 |
+
" & followed_by_closing_round_paren",
|
148 |
+
),
|
149 |
+
Binding(
|
150 |
+
match.delete_pair,
|
151 |
+
["backspace"],
|
152 |
+
"focused_insert"
|
153 |
+
" & preceded_by_opening_bracket"
|
154 |
+
" & auto_match"
|
155 |
+
" & followed_by_closing_bracket",
|
156 |
+
),
|
157 |
+
Binding(
|
158 |
+
match.delete_pair,
|
159 |
+
["backspace"],
|
160 |
+
"focused_insert"
|
161 |
+
" & preceded_by_opening_brace"
|
162 |
+
" & auto_match"
|
163 |
+
" & followed_by_closing_brace",
|
164 |
+
),
|
165 |
+
Binding(
|
166 |
+
match.delete_pair,
|
167 |
+
["backspace"],
|
168 |
+
"focused_insert"
|
169 |
+
" & preceded_by_double_quote"
|
170 |
+
" & auto_match"
|
171 |
+
" & followed_by_double_quote",
|
172 |
+
),
|
173 |
+
Binding(
|
174 |
+
match.delete_pair,
|
175 |
+
["backspace"],
|
176 |
+
"focused_insert"
|
177 |
+
" & preceded_by_single_quote"
|
178 |
+
" & auto_match"
|
179 |
+
" & followed_by_single_quote",
|
180 |
+
),
|
181 |
+
]
|
182 |
+
|
183 |
+
AUTO_SUGGEST_BINDINGS = [
|
184 |
+
# there are two reasons for re-defining bindings defined upstream:
|
185 |
+
# 1) prompt-toolkit does not execute autosuggestion bindings in vi mode,
|
186 |
+
# 2) prompt-toolkit checks if we are at the end of text, not end of line
|
187 |
+
# hence it does not work in multi-line mode of navigable provider
|
188 |
+
Binding(
|
189 |
+
auto_suggest.accept_or_jump_to_end,
|
190 |
+
["end"],
|
191 |
+
"has_suggestion & default_buffer_focused & emacs_like_insert_mode",
|
192 |
+
),
|
193 |
+
Binding(
|
194 |
+
auto_suggest.accept_or_jump_to_end,
|
195 |
+
["c-e"],
|
196 |
+
"has_suggestion & default_buffer_focused & emacs_like_insert_mode",
|
197 |
+
),
|
198 |
+
Binding(
|
199 |
+
auto_suggest.accept,
|
200 |
+
["c-f"],
|
201 |
+
"has_suggestion & default_buffer_focused & emacs_like_insert_mode",
|
202 |
+
),
|
203 |
+
Binding(
|
204 |
+
auto_suggest.accept,
|
205 |
+
["right"],
|
206 |
+
"has_suggestion & default_buffer_focused & emacs_like_insert_mode & is_cursor_at_the_end_of_line",
|
207 |
+
),
|
208 |
+
Binding(
|
209 |
+
auto_suggest.accept_word,
|
210 |
+
["escape", "f"],
|
211 |
+
"has_suggestion & default_buffer_focused & emacs_like_insert_mode",
|
212 |
+
),
|
213 |
+
Binding(
|
214 |
+
auto_suggest.accept_token,
|
215 |
+
["c-right"],
|
216 |
+
"has_suggestion & default_buffer_focused & emacs_like_insert_mode",
|
217 |
+
),
|
218 |
+
Binding(
|
219 |
+
auto_suggest.discard,
|
220 |
+
["escape"],
|
221 |
+
# note this one is using `emacs_insert_mode`, not `emacs_like_insert_mode`
|
222 |
+
# as in `vi_insert_mode` we do not want `escape` to be shadowed (ever).
|
223 |
+
"has_suggestion & default_buffer_focused & emacs_insert_mode",
|
224 |
+
),
|
225 |
+
Binding(
|
226 |
+
auto_suggest.discard,
|
227 |
+
["delete"],
|
228 |
+
"has_suggestion & default_buffer_focused & emacs_insert_mode",
|
229 |
+
),
|
230 |
+
Binding(
|
231 |
+
auto_suggest.swap_autosuggestion_up,
|
232 |
+
["c-up"],
|
233 |
+
"navigable_suggestions"
|
234 |
+
" & ~has_line_above"
|
235 |
+
" & has_suggestion"
|
236 |
+
" & default_buffer_focused",
|
237 |
+
),
|
238 |
+
Binding(
|
239 |
+
auto_suggest.swap_autosuggestion_down,
|
240 |
+
["c-down"],
|
241 |
+
"navigable_suggestions"
|
242 |
+
" & ~has_line_below"
|
243 |
+
" & has_suggestion"
|
244 |
+
" & default_buffer_focused",
|
245 |
+
),
|
246 |
+
Binding(
|
247 |
+
auto_suggest.up_and_update_hint,
|
248 |
+
["c-up"],
|
249 |
+
"has_line_above & navigable_suggestions & default_buffer_focused",
|
250 |
+
),
|
251 |
+
Binding(
|
252 |
+
auto_suggest.down_and_update_hint,
|
253 |
+
["c-down"],
|
254 |
+
"has_line_below & navigable_suggestions & default_buffer_focused",
|
255 |
+
),
|
256 |
+
Binding(
|
257 |
+
auto_suggest.accept_character,
|
258 |
+
["escape", "right"],
|
259 |
+
"has_suggestion & default_buffer_focused & emacs_like_insert_mode",
|
260 |
+
),
|
261 |
+
Binding(
|
262 |
+
auto_suggest.accept_and_move_cursor_left,
|
263 |
+
["c-left"],
|
264 |
+
"has_suggestion & default_buffer_focused & emacs_like_insert_mode",
|
265 |
+
),
|
266 |
+
Binding(
|
267 |
+
auto_suggest.accept_and_keep_cursor,
|
268 |
+
["escape", "down"],
|
269 |
+
"has_suggestion & default_buffer_focused & emacs_insert_mode",
|
270 |
+
),
|
271 |
+
Binding(
|
272 |
+
auto_suggest.backspace_and_resume_hint,
|
273 |
+
["backspace"],
|
274 |
+
# no `has_suggestion` here to allow resuming if no suggestion
|
275 |
+
"default_buffer_focused & emacs_like_insert_mode",
|
276 |
+
),
|
277 |
+
Binding(
|
278 |
+
auto_suggest.resume_hinting,
|
279 |
+
["right"],
|
280 |
+
"is_cursor_at_the_end_of_line"
|
281 |
+
" & default_buffer_focused"
|
282 |
+
" & emacs_like_insert_mode"
|
283 |
+
" & pass_through",
|
284 |
+
),
|
285 |
+
]
|
286 |
+
|
287 |
+
|
288 |
+
SIMPLE_CONTROL_BINDINGS = [
|
289 |
+
Binding(cmd, [key], "vi_insert_mode & default_buffer_focused & ebivim")
|
290 |
+
for key, cmd in {
|
291 |
+
"c-a": nc.beginning_of_line,
|
292 |
+
"c-b": nc.backward_char,
|
293 |
+
"c-k": nc.kill_line,
|
294 |
+
"c-w": nc.backward_kill_word,
|
295 |
+
"c-y": nc.yank,
|
296 |
+
"c-_": nc.undo,
|
297 |
+
}.items()
|
298 |
+
]
|
299 |
+
|
300 |
+
|
301 |
+
ALT_AND_COMOBO_CONTROL_BINDINGS = [
|
302 |
+
Binding(cmd, list(keys), "vi_insert_mode & default_buffer_focused & ebivim")
|
303 |
+
for keys, cmd in {
|
304 |
+
# Control Combos
|
305 |
+
("c-x", "c-e"): nc.edit_and_execute,
|
306 |
+
("c-x", "e"): nc.edit_and_execute,
|
307 |
+
# Alt
|
308 |
+
("escape", "b"): nc.backward_word,
|
309 |
+
("escape", "c"): nc.capitalize_word,
|
310 |
+
("escape", "d"): nc.kill_word,
|
311 |
+
("escape", "h"): nc.backward_kill_word,
|
312 |
+
("escape", "l"): nc.downcase_word,
|
313 |
+
("escape", "u"): nc.uppercase_word,
|
314 |
+
("escape", "y"): nc.yank_pop,
|
315 |
+
("escape", "."): nc.yank_last_arg,
|
316 |
+
}.items()
|
317 |
+
]
|
318 |
+
|
319 |
+
|
320 |
+
def add_binding(bindings: KeyBindings, binding: Binding):
|
321 |
+
bindings.add(
|
322 |
+
*binding.keys,
|
323 |
+
**({"filter": binding.filter} if binding.filter is not None else {}),
|
324 |
+
)(binding.command)
|
325 |
+
|
326 |
+
|
327 |
+
def create_ipython_shortcuts(shell, skip=None) -> KeyBindings:
|
328 |
+
"""Set up the prompt_toolkit keyboard shortcuts for IPython.
|
329 |
+
|
330 |
+
Parameters
|
331 |
+
----------
|
332 |
+
shell: InteractiveShell
|
333 |
+
The current IPython shell Instance
|
334 |
+
skip: List[Binding]
|
335 |
+
Bindings to skip.
|
336 |
+
|
337 |
+
Returns
|
338 |
+
-------
|
339 |
+
KeyBindings
|
340 |
+
the keybinding instance for prompt toolkit.
|
341 |
+
|
342 |
+
"""
|
343 |
+
kb = KeyBindings()
|
344 |
+
skip = skip or []
|
345 |
+
for binding in KEY_BINDINGS:
|
346 |
+
skip_this_one = False
|
347 |
+
for to_skip in skip:
|
348 |
+
if (
|
349 |
+
to_skip.command == binding.command
|
350 |
+
and to_skip.filter == binding.filter
|
351 |
+
and to_skip.keys == binding.keys
|
352 |
+
):
|
353 |
+
skip_this_one = True
|
354 |
+
break
|
355 |
+
if skip_this_one:
|
356 |
+
continue
|
357 |
+
add_binding(kb, binding)
|
358 |
+
|
359 |
+
def get_input_mode(self):
|
360 |
+
app = get_app()
|
361 |
+
app.ttimeoutlen = shell.ttimeoutlen
|
362 |
+
app.timeoutlen = shell.timeoutlen
|
363 |
+
|
364 |
+
return self._input_mode
|
365 |
+
|
366 |
+
def set_input_mode(self, mode):
|
367 |
+
shape = {InputMode.NAVIGATION: 2, InputMode.REPLACE: 4}.get(mode, 6)
|
368 |
+
cursor = "\x1b[{} q".format(shape)
|
369 |
+
|
370 |
+
sys.stdout.write(cursor)
|
371 |
+
sys.stdout.flush()
|
372 |
+
|
373 |
+
self._input_mode = mode
|
374 |
+
|
375 |
+
if shell.editing_mode == "vi" and shell.modal_cursor:
|
376 |
+
ViState._input_mode = InputMode.INSERT # type: ignore
|
377 |
+
ViState.input_mode = property(get_input_mode, set_input_mode) # type: ignore
|
378 |
+
|
379 |
+
return kb
|
380 |
+
|
381 |
+
|
382 |
+
def reformat_and_execute(event):
|
383 |
+
"""Reformat code and execute it"""
|
384 |
+
shell = get_ipython()
|
385 |
+
reformat_text_before_cursor(
|
386 |
+
event.current_buffer, event.current_buffer.document, shell
|
387 |
+
)
|
388 |
+
event.current_buffer.validate_and_handle()
|
389 |
+
|
390 |
+
|
391 |
+
def reformat_text_before_cursor(buffer, document, shell):
|
392 |
+
text = buffer.delete_before_cursor(len(document.text[: document.cursor_position]))
|
393 |
+
try:
|
394 |
+
formatted_text = shell.reformat_handler(text)
|
395 |
+
buffer.insert_text(formatted_text)
|
396 |
+
except Exception as e:
|
397 |
+
buffer.insert_text(text)
|
398 |
+
|
399 |
+
|
400 |
+
def handle_return_or_newline_or_execute(event):
|
401 |
+
shell = get_ipython()
|
402 |
+
if getattr(shell, "handle_return", None):
|
403 |
+
return shell.handle_return(shell)(event)
|
404 |
+
else:
|
405 |
+
return newline_or_execute_outer(shell)(event)
|
406 |
+
|
407 |
+
|
408 |
+
def newline_or_execute_outer(shell):
|
409 |
+
def newline_or_execute(event):
|
410 |
+
"""When the user presses return, insert a newline or execute the code."""
|
411 |
+
b = event.current_buffer
|
412 |
+
d = b.document
|
413 |
+
|
414 |
+
if b.complete_state:
|
415 |
+
cc = b.complete_state.current_completion
|
416 |
+
if cc:
|
417 |
+
b.apply_completion(cc)
|
418 |
+
else:
|
419 |
+
b.cancel_completion()
|
420 |
+
return
|
421 |
+
|
422 |
+
# If there's only one line, treat it as if the cursor is at the end.
|
423 |
+
# See https://github.com/ipython/ipython/issues/10425
|
424 |
+
if d.line_count == 1:
|
425 |
+
check_text = d.text
|
426 |
+
else:
|
427 |
+
check_text = d.text[: d.cursor_position]
|
428 |
+
status, indent = shell.check_complete(check_text)
|
429 |
+
|
430 |
+
# if all we have after the cursor is whitespace: reformat current text
|
431 |
+
# before cursor
|
432 |
+
after_cursor = d.text[d.cursor_position :]
|
433 |
+
reformatted = False
|
434 |
+
if not after_cursor.strip():
|
435 |
+
reformat_text_before_cursor(b, d, shell)
|
436 |
+
reformatted = True
|
437 |
+
if not (
|
438 |
+
d.on_last_line
|
439 |
+
or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
|
440 |
+
):
|
441 |
+
if shell.autoindent:
|
442 |
+
b.insert_text("\n" + indent)
|
443 |
+
else:
|
444 |
+
b.insert_text("\n")
|
445 |
+
return
|
446 |
+
|
447 |
+
if (status != "incomplete") and b.accept_handler:
|
448 |
+
if not reformatted:
|
449 |
+
reformat_text_before_cursor(b, d, shell)
|
450 |
+
b.validate_and_handle()
|
451 |
+
else:
|
452 |
+
if shell.autoindent:
|
453 |
+
b.insert_text("\n" + indent)
|
454 |
+
else:
|
455 |
+
b.insert_text("\n")
|
456 |
+
|
457 |
+
return newline_or_execute
|
458 |
+
|
459 |
+
|
460 |
+
def previous_history_or_previous_completion(event):
|
461 |
+
"""
|
462 |
+
Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
|
463 |
+
|
464 |
+
If completer is open this still select previous completion.
|
465 |
+
"""
|
466 |
+
event.current_buffer.auto_up()
|
467 |
+
|
468 |
+
|
469 |
+
def next_history_or_next_completion(event):
|
470 |
+
"""
|
471 |
+
Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
|
472 |
+
|
473 |
+
If completer is open this still select next completion.
|
474 |
+
"""
|
475 |
+
event.current_buffer.auto_down()
|
476 |
+
|
477 |
+
|
478 |
+
def dismiss_completion(event):
|
479 |
+
"""Dismiss completion"""
|
480 |
+
b = event.current_buffer
|
481 |
+
if b.complete_state:
|
482 |
+
b.cancel_completion()
|
483 |
+
|
484 |
+
|
485 |
+
def reset_buffer(event):
|
486 |
+
"""Reset buffer"""
|
487 |
+
b = event.current_buffer
|
488 |
+
if b.complete_state:
|
489 |
+
b.cancel_completion()
|
490 |
+
else:
|
491 |
+
b.reset()
|
492 |
+
|
493 |
+
|
494 |
+
def reset_search_buffer(event):
|
495 |
+
"""Reset search buffer"""
|
496 |
+
if event.current_buffer.document.text:
|
497 |
+
event.current_buffer.reset()
|
498 |
+
else:
|
499 |
+
event.app.layout.focus(DEFAULT_BUFFER)
|
500 |
+
|
501 |
+
|
502 |
+
def suspend_to_bg(event):
|
503 |
+
"""Suspend to background"""
|
504 |
+
event.app.suspend_to_background()
|
505 |
+
|
506 |
+
|
507 |
+
def quit(event):
|
508 |
+
"""
|
509 |
+
Quit application with ``SIGQUIT`` if supported or ``sys.exit`` otherwise.
|
510 |
+
|
511 |
+
On platforms that support SIGQUIT, send SIGQUIT to the current process.
|
512 |
+
On other platforms, just exit the process with a message.
|
513 |
+
"""
|
514 |
+
sigquit = getattr(signal, "SIGQUIT", None)
|
515 |
+
if sigquit is not None:
|
516 |
+
os.kill(0, signal.SIGQUIT)
|
517 |
+
else:
|
518 |
+
sys.exit("Quit")
|
519 |
+
|
520 |
+
|
521 |
+
def indent_buffer(event):
|
522 |
+
"""Indent buffer"""
|
523 |
+
event.current_buffer.insert_text(" " * 4)
|
524 |
+
|
525 |
+
|
526 |
+
def newline_autoindent(event):
|
527 |
+
"""Insert a newline after the cursor indented appropriately.
|
528 |
+
|
529 |
+
Fancier version of former ``newline_with_copy_margin`` which should
|
530 |
+
compute the correct indentation of the inserted line. That is to say, indent
|
531 |
+
by 4 extra space after a function definition, class definition, context
|
532 |
+
manager... And dedent by 4 space after ``pass``, ``return``, ``raise ...``.
|
533 |
+
"""
|
534 |
+
shell = get_ipython()
|
535 |
+
inputsplitter = shell.input_transformer_manager
|
536 |
+
b = event.current_buffer
|
537 |
+
d = b.document
|
538 |
+
|
539 |
+
if b.complete_state:
|
540 |
+
b.cancel_completion()
|
541 |
+
text = d.text[: d.cursor_position] + "\n"
|
542 |
+
_, indent = inputsplitter.check_complete(text)
|
543 |
+
b.insert_text("\n" + (" " * (indent or 0)), move_cursor=False)
|
544 |
+
|
545 |
+
|
546 |
+
def open_input_in_editor(event):
|
547 |
+
"""Open code from input in external editor"""
|
548 |
+
event.app.current_buffer.open_in_editor()
|
549 |
+
|
550 |
+
|
551 |
+
if sys.platform == "win32":
|
552 |
+
from IPython.core.error import TryNext
|
553 |
+
from IPython.lib.clipboard import (
|
554 |
+
ClipboardEmpty,
|
555 |
+
tkinter_clipboard_get,
|
556 |
+
win32_clipboard_get,
|
557 |
+
)
|
558 |
+
|
559 |
+
@undoc
|
560 |
+
def win_paste(event):
|
561 |
+
try:
|
562 |
+
text = win32_clipboard_get()
|
563 |
+
except TryNext:
|
564 |
+
try:
|
565 |
+
text = tkinter_clipboard_get()
|
566 |
+
except (TryNext, ClipboardEmpty):
|
567 |
+
return
|
568 |
+
except ClipboardEmpty:
|
569 |
+
return
|
570 |
+
event.current_buffer.insert_text(text.replace("\t", " " * 4))
|
571 |
+
|
572 |
+
else:
|
573 |
+
|
574 |
+
@undoc
|
575 |
+
def win_paste(event):
|
576 |
+
"""Stub used on other platforms"""
|
577 |
+
pass
|
578 |
+
|
579 |
+
|
580 |
+
KEY_BINDINGS = [
|
581 |
+
Binding(
|
582 |
+
handle_return_or_newline_or_execute,
|
583 |
+
["enter"],
|
584 |
+
"default_buffer_focused & ~has_selection & insert_mode",
|
585 |
+
),
|
586 |
+
Binding(
|
587 |
+
reformat_and_execute,
|
588 |
+
["escape", "enter"],
|
589 |
+
"default_buffer_focused & ~has_selection & insert_mode & ebivim",
|
590 |
+
),
|
591 |
+
Binding(quit, ["c-\\"]),
|
592 |
+
Binding(
|
593 |
+
previous_history_or_previous_completion,
|
594 |
+
["c-p"],
|
595 |
+
"vi_insert_mode & default_buffer_focused",
|
596 |
+
),
|
597 |
+
Binding(
|
598 |
+
next_history_or_next_completion,
|
599 |
+
["c-n"],
|
600 |
+
"vi_insert_mode & default_buffer_focused",
|
601 |
+
),
|
602 |
+
Binding(dismiss_completion, ["c-g"], "default_buffer_focused & has_completions"),
|
603 |
+
Binding(reset_buffer, ["c-c"], "default_buffer_focused"),
|
604 |
+
Binding(reset_search_buffer, ["c-c"], "search_buffer_focused"),
|
605 |
+
Binding(suspend_to_bg, ["c-z"], "supports_suspend"),
|
606 |
+
Binding(
|
607 |
+
indent_buffer,
|
608 |
+
["tab"], # Ctrl+I == Tab
|
609 |
+
"default_buffer_focused & ~has_selection & insert_mode & cursor_in_leading_ws",
|
610 |
+
),
|
611 |
+
Binding(newline_autoindent, ["c-o"], "default_buffer_focused & emacs_insert_mode"),
|
612 |
+
Binding(open_input_in_editor, ["f2"], "default_buffer_focused"),
|
613 |
+
*AUTO_MATCH_BINDINGS,
|
614 |
+
*AUTO_SUGGEST_BINDINGS,
|
615 |
+
Binding(
|
616 |
+
display_completions_like_readline,
|
617 |
+
["c-i"],
|
618 |
+
"readline_like_completions"
|
619 |
+
" & default_buffer_focused"
|
620 |
+
" & ~has_selection"
|
621 |
+
" & insert_mode"
|
622 |
+
" & ~cursor_in_leading_ws",
|
623 |
+
),
|
624 |
+
Binding(win_paste, ["c-v"], "default_buffer_focused & ~vi_mode & is_windows_os"),
|
625 |
+
*SIMPLE_CONTROL_BINDINGS,
|
626 |
+
*ALT_AND_COMOBO_CONTROL_BINDINGS,
|
627 |
+
]
|
628 |
+
|
629 |
+
UNASSIGNED_ALLOWED_COMMANDS = [
|
630 |
+
auto_suggest.llm_autosuggestion,
|
631 |
+
nc.beginning_of_buffer,
|
632 |
+
nc.end_of_buffer,
|
633 |
+
nc.end_of_line,
|
634 |
+
nc.forward_word,
|
635 |
+
nc.unix_line_discard,
|
636 |
+
]
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/shortcuts/__pycache__/__init__.cpython-313.pyc
ADDED
Binary file (22.3 kB). View file
|
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/shortcuts/__pycache__/auto_match.cpython-313.pyc
ADDED
Binary file (4.86 kB). View file
|
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/shortcuts/__pycache__/auto_suggest.cpython-313.pyc
ADDED
Binary file (29.5 kB). View file
|
|
temp_venv/lib/python3.13/site-packages/IPython/terminal/shortcuts/__pycache__/filters.cpython-313.pyc
ADDED
Binary file (13.9 kB). View file
|
|