Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- lib/python3.12/site-packages/IPython/core/magics/__init__.py +42 -0
- lib/python3.12/site-packages/IPython/core/magics/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/core/magics/__pycache__/ast_mod.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/core/magics/__pycache__/auto.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/core/magics/__pycache__/basic.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/core/magics/__pycache__/code.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/core/magics/__pycache__/config.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/core/magics/__pycache__/display.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/core/magics/__pycache__/execution.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/core/magics/__pycache__/extension.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/core/magics/__pycache__/history.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/core/magics/__pycache__/logging.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/core/magics/__pycache__/namespace.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/core/magics/__pycache__/osm.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/core/magics/__pycache__/packaging.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/core/magics/__pycache__/pylab.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/core/magics/__pycache__/script.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/core/magics/ast_mod.py +330 -0
- lib/python3.12/site-packages/IPython/core/magics/auto.py +144 -0
- lib/python3.12/site-packages/IPython/core/magics/config.py +140 -0
- lib/python3.12/site-packages/IPython/core/magics/display.py +93 -0
- lib/python3.12/site-packages/IPython/core/magics/execution.py +1707 -0
- lib/python3.12/site-packages/IPython/core/magics/history.py +361 -0
- lib/python3.12/site-packages/IPython/core/magics/namespace.py +723 -0
- lib/python3.12/site-packages/IPython/core/magics/osm.py +855 -0
- lib/python3.12/site-packages/IPython/core/magics/packaging.py +181 -0
- lib/python3.12/site-packages/IPython/core/magics/pylab.py +173 -0
- lib/python3.12/site-packages/IPython/external/__init__.py +7 -0
- lib/python3.12/site-packages/IPython/external/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/external/__pycache__/pickleshare.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/external/__pycache__/qt_for_kernel.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/external/__pycache__/qt_loaders.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/external/pickleshare.py +303 -0
- lib/python3.12/site-packages/IPython/external/qt_for_kernel.py +124 -0
- lib/python3.12/site-packages/IPython/external/qt_loaders.py +423 -0
- lib/python3.12/site-packages/IPython/utils/PyColorize.py +577 -0
- lib/python3.12/site-packages/IPython/utils/__init__.py +0 -0
- lib/python3.12/site-packages/IPython/utils/__pycache__/PyColorize.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/utils/__pycache__/_process_cli.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/utils/__pycache__/_process_common.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/utils/__pycache__/_process_emscripten.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/utils/__pycache__/_process_posix.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/utils/__pycache__/_process_win32.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/utils/__pycache__/_process_win32_controller.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/utils/__pycache__/_sysinfo.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/utils/__pycache__/coloransi.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/utils/__pycache__/contexts.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/utils/__pycache__/data.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/utils/__pycache__/docs.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/IPython/utils/__pycache__/eventful.cpython-312.pyc +0 -0
lib/python3.12/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 |
+
"""
|
lib/python3.12/site-packages/IPython/core/magics/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (1.48 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/core/magics/__pycache__/ast_mod.cpython-312.pyc
ADDED
|
Binary file (13.2 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/core/magics/__pycache__/auto.cpython-312.pyc
ADDED
|
Binary file (5.39 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/core/magics/__pycache__/basic.cpython-312.pyc
ADDED
|
Binary file (31.7 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/core/magics/__pycache__/code.cpython-312.pyc
ADDED
|
Binary file (29.1 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/core/magics/__pycache__/config.cpython-312.pyc
ADDED
|
Binary file (5.09 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/core/magics/__pycache__/display.cpython-312.pyc
ADDED
|
Binary file (3.68 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/core/magics/__pycache__/execution.cpython-312.pyc
ADDED
|
Binary file (67.6 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/core/magics/__pycache__/extension.cpython-312.pyc
ADDED
|
Binary file (2.61 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/core/magics/__pycache__/history.cpython-312.pyc
ADDED
|
Binary file (13.8 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/core/magics/__pycache__/logging.cpython-312.pyc
ADDED
|
Binary file (7.07 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/core/magics/__pycache__/namespace.cpython-312.pyc
ADDED
|
Binary file (26.7 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/core/magics/__pycache__/osm.cpython-312.pyc
ADDED
|
Binary file (36.2 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/core/magics/__pycache__/packaging.cpython-312.pyc
ADDED
|
Binary file (6.97 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/core/magics/__pycache__/pylab.cpython-312.pyc
ADDED
|
Binary file (6.95 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/core/magics/__pycache__/script.cpython-312.pyc
ADDED
|
Binary file (18.1 kB). View file
|
|
|
lib/python3.12/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)
|
lib/python3.12/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])
|
lib/python3.12/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)
|
lib/python3.12/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))
|
lib/python3.12/site-packages/IPython/core/magics/execution.py
ADDED
|
@@ -0,0 +1,1707 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
import signal
|
| 24 |
+
from typing import Dict, Any
|
| 25 |
+
from ast import (
|
| 26 |
+
Assign,
|
| 27 |
+
Call,
|
| 28 |
+
Expr,
|
| 29 |
+
Load,
|
| 30 |
+
Module,
|
| 31 |
+
Name,
|
| 32 |
+
NodeTransformer,
|
| 33 |
+
Store,
|
| 34 |
+
parse,
|
| 35 |
+
unparse,
|
| 36 |
+
)
|
| 37 |
+
from io import StringIO
|
| 38 |
+
from logging import error
|
| 39 |
+
from pathlib import Path
|
| 40 |
+
from pdb import Restart
|
| 41 |
+
from textwrap import dedent, indent
|
| 42 |
+
from warnings import warn
|
| 43 |
+
|
| 44 |
+
from IPython.core import magic_arguments, oinspect, page
|
| 45 |
+
from IPython.core.displayhook import DisplayHook
|
| 46 |
+
from IPython.core.error import UsageError
|
| 47 |
+
from IPython.core.macro import Macro
|
| 48 |
+
from IPython.core.magic import (
|
| 49 |
+
Magics,
|
| 50 |
+
cell_magic,
|
| 51 |
+
line_cell_magic,
|
| 52 |
+
line_magic,
|
| 53 |
+
magics_class,
|
| 54 |
+
needs_local_scope,
|
| 55 |
+
no_var_expand,
|
| 56 |
+
on_off,
|
| 57 |
+
output_can_be_silenced,
|
| 58 |
+
)
|
| 59 |
+
from IPython.testing.skipdoctest import skip_doctest
|
| 60 |
+
from IPython.utils.capture import capture_output
|
| 61 |
+
from IPython.utils.contexts import preserve_keys
|
| 62 |
+
from IPython.utils.ipstruct import Struct
|
| 63 |
+
from IPython.utils.module_paths import find_mod
|
| 64 |
+
from IPython.utils.path import get_py_filename, shellglob
|
| 65 |
+
from IPython.utils.timing import clock, clock2
|
| 66 |
+
from IPython.core.magics.ast_mod import ReplaceCodeTransformer
|
| 67 |
+
|
| 68 |
+
#-----------------------------------------------------------------------------
|
| 69 |
+
# Magic implementation classes
|
| 70 |
+
#-----------------------------------------------------------------------------
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
class TimeitResult:
|
| 74 |
+
"""
|
| 75 |
+
Object returned by the timeit magic with info about the run.
|
| 76 |
+
|
| 77 |
+
Contains the following attributes:
|
| 78 |
+
|
| 79 |
+
loops: int
|
| 80 |
+
number of loops done per measurement
|
| 81 |
+
|
| 82 |
+
repeat: int
|
| 83 |
+
number of times the measurement was repeated
|
| 84 |
+
|
| 85 |
+
best: float
|
| 86 |
+
best execution time / number
|
| 87 |
+
|
| 88 |
+
all_runs : list[float]
|
| 89 |
+
execution time of each run (in s)
|
| 90 |
+
|
| 91 |
+
compile_time: float
|
| 92 |
+
time of statement compilation (s)
|
| 93 |
+
|
| 94 |
+
"""
|
| 95 |
+
def __init__(self, loops, repeat, best, worst, all_runs, compile_time, precision):
|
| 96 |
+
self.loops = loops
|
| 97 |
+
self.repeat = repeat
|
| 98 |
+
self.best = best
|
| 99 |
+
self.worst = worst
|
| 100 |
+
self.all_runs = all_runs
|
| 101 |
+
self.compile_time = compile_time
|
| 102 |
+
self._precision = precision
|
| 103 |
+
self.timings = [dt / self.loops for dt in all_runs]
|
| 104 |
+
|
| 105 |
+
@property
|
| 106 |
+
def average(self):
|
| 107 |
+
return math.fsum(self.timings) / len(self.timings)
|
| 108 |
+
|
| 109 |
+
@property
|
| 110 |
+
def stdev(self):
|
| 111 |
+
mean = self.average
|
| 112 |
+
return (math.fsum([(x - mean) ** 2 for x in self.timings]) / len(self.timings)) ** 0.5
|
| 113 |
+
|
| 114 |
+
def __str__(self):
|
| 115 |
+
pm = '+-'
|
| 116 |
+
if hasattr(sys.stdout, 'encoding') and sys.stdout.encoding:
|
| 117 |
+
try:
|
| 118 |
+
"\xb1".encode(sys.stdout.encoding)
|
| 119 |
+
pm = "\xb1"
|
| 120 |
+
except:
|
| 121 |
+
pass
|
| 122 |
+
return "{mean} {pm} {std} per loop (mean {pm} std. dev. of {runs} run{run_plural}, {loops:,} loop{loop_plural} each)".format(
|
| 123 |
+
pm=pm,
|
| 124 |
+
runs=self.repeat,
|
| 125 |
+
loops=self.loops,
|
| 126 |
+
loop_plural="" if self.loops == 1 else "s",
|
| 127 |
+
run_plural="" if self.repeat == 1 else "s",
|
| 128 |
+
mean=_format_time(self.average, self._precision),
|
| 129 |
+
std=_format_time(self.stdev, self._precision),
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
+
def _repr_pretty_(self, p , cycle):
|
| 133 |
+
unic = self.__str__()
|
| 134 |
+
p.text("<TimeitResult : " + unic + ">")
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
class TimeitTemplateFiller(ast.NodeTransformer):
|
| 138 |
+
"""Fill in the AST template for timing execution.
|
| 139 |
+
|
| 140 |
+
This is quite closely tied to the template definition, which is in
|
| 141 |
+
:meth:`ExecutionMagics.timeit`.
|
| 142 |
+
"""
|
| 143 |
+
def __init__(self, ast_setup, ast_stmt):
|
| 144 |
+
self.ast_setup = ast_setup
|
| 145 |
+
self.ast_stmt = ast_stmt
|
| 146 |
+
|
| 147 |
+
def visit_FunctionDef(self, node):
|
| 148 |
+
"Fill in the setup statement"
|
| 149 |
+
self.generic_visit(node)
|
| 150 |
+
if node.name == "inner":
|
| 151 |
+
node.body[:1] = self.ast_setup.body
|
| 152 |
+
|
| 153 |
+
return node
|
| 154 |
+
|
| 155 |
+
def visit_For(self, node):
|
| 156 |
+
"Fill in the statement to be timed"
|
| 157 |
+
if getattr(getattr(node.body[0], 'value', None), 'id', None) == 'stmt':
|
| 158 |
+
node.body = self.ast_stmt.body
|
| 159 |
+
return node
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
class Timer(timeit.Timer):
|
| 163 |
+
"""Timer class that explicitly uses self.inner
|
| 164 |
+
|
| 165 |
+
which is an undocumented implementation detail of CPython,
|
| 166 |
+
not shared by PyPy.
|
| 167 |
+
"""
|
| 168 |
+
|
| 169 |
+
# Timer.timeit copied from CPython 3.4.2
|
| 170 |
+
def timeit(self, number=timeit.default_number):
|
| 171 |
+
"""Time 'number' executions of the main statement.
|
| 172 |
+
|
| 173 |
+
To be precise, this executes the setup statement once, and
|
| 174 |
+
then returns the time it takes to execute the main statement
|
| 175 |
+
a number of times, as a float measured in seconds. The
|
| 176 |
+
argument is the number of times through the loop, defaulting
|
| 177 |
+
to one million. The main statement, the setup statement and
|
| 178 |
+
the timer function to be used are passed to the constructor.
|
| 179 |
+
"""
|
| 180 |
+
it = itertools.repeat(None, number)
|
| 181 |
+
gcold = gc.isenabled()
|
| 182 |
+
gc.disable()
|
| 183 |
+
try:
|
| 184 |
+
timing = self.inner(it, self.timer)
|
| 185 |
+
finally:
|
| 186 |
+
if gcold:
|
| 187 |
+
gc.enable()
|
| 188 |
+
return timing
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
@magics_class
|
| 192 |
+
class ExecutionMagics(Magics):
|
| 193 |
+
"""Magics related to code execution, debugging, profiling, etc."""
|
| 194 |
+
|
| 195 |
+
_transformers: Dict[str, Any] = {}
|
| 196 |
+
|
| 197 |
+
def __init__(self, shell):
|
| 198 |
+
super(ExecutionMagics, self).__init__(shell)
|
| 199 |
+
# Default execution function used to actually run user code.
|
| 200 |
+
self.default_runner = None
|
| 201 |
+
|
| 202 |
+
@skip_doctest
|
| 203 |
+
@no_var_expand
|
| 204 |
+
@line_cell_magic
|
| 205 |
+
def prun(self, parameter_s='', cell=None):
|
| 206 |
+
"""Run a statement through the python code profiler.
|
| 207 |
+
|
| 208 |
+
**Usage, in line mode**::
|
| 209 |
+
|
| 210 |
+
%prun [options] statement
|
| 211 |
+
|
| 212 |
+
**Usage, in cell mode**::
|
| 213 |
+
|
| 214 |
+
%%prun [options] [statement]
|
| 215 |
+
code...
|
| 216 |
+
code...
|
| 217 |
+
|
| 218 |
+
In cell mode, the additional code lines are appended to the (possibly
|
| 219 |
+
empty) statement in the first line. Cell mode allows you to easily
|
| 220 |
+
profile multiline blocks without having to put them in a separate
|
| 221 |
+
function.
|
| 222 |
+
|
| 223 |
+
The given statement (which doesn't require quote marks) is run via the
|
| 224 |
+
python profiler in a manner similar to the profile.run() function.
|
| 225 |
+
Namespaces are internally managed to work correctly; profile.run
|
| 226 |
+
cannot be used in IPython because it makes certain assumptions about
|
| 227 |
+
namespaces which do not hold under IPython.
|
| 228 |
+
|
| 229 |
+
Options:
|
| 230 |
+
|
| 231 |
+
-l <limit>
|
| 232 |
+
you can place restrictions on what or how much of the
|
| 233 |
+
profile gets printed. The limit value can be:
|
| 234 |
+
|
| 235 |
+
* A string: only information for function names containing this string
|
| 236 |
+
is printed.
|
| 237 |
+
|
| 238 |
+
* An integer: only these many lines are printed.
|
| 239 |
+
|
| 240 |
+
* A float (between 0 and 1): this fraction of the report is printed
|
| 241 |
+
(for example, use a limit of 0.4 to see the topmost 40% only).
|
| 242 |
+
|
| 243 |
+
You can combine several limits with repeated use of the option. For
|
| 244 |
+
example, ``-l __init__ -l 5`` will print only the topmost 5 lines of
|
| 245 |
+
information about class constructors.
|
| 246 |
+
|
| 247 |
+
-r
|
| 248 |
+
return the pstats.Stats object generated by the profiling. This
|
| 249 |
+
object has all the information about the profile in it, and you can
|
| 250 |
+
later use it for further analysis or in other functions.
|
| 251 |
+
|
| 252 |
+
-s <key>
|
| 253 |
+
sort profile by given key. You can provide more than one key
|
| 254 |
+
by using the option several times: '-s key1 -s key2 -s key3...'. The
|
| 255 |
+
default sorting key is 'time'.
|
| 256 |
+
|
| 257 |
+
The following is copied verbatim from the profile documentation
|
| 258 |
+
referenced below:
|
| 259 |
+
|
| 260 |
+
When more than one key is provided, additional keys are used as
|
| 261 |
+
secondary criteria when the there is equality in all keys selected
|
| 262 |
+
before them.
|
| 263 |
+
|
| 264 |
+
Abbreviations can be used for any key names, as long as the
|
| 265 |
+
abbreviation is unambiguous. The following are the keys currently
|
| 266 |
+
defined:
|
| 267 |
+
|
| 268 |
+
============ =====================
|
| 269 |
+
Valid Arg Meaning
|
| 270 |
+
============ =====================
|
| 271 |
+
"calls" call count
|
| 272 |
+
"cumulative" cumulative time
|
| 273 |
+
"file" file name
|
| 274 |
+
"module" file name
|
| 275 |
+
"pcalls" primitive call count
|
| 276 |
+
"line" line number
|
| 277 |
+
"name" function name
|
| 278 |
+
"nfl" name/file/line
|
| 279 |
+
"stdname" standard name
|
| 280 |
+
"time" internal time
|
| 281 |
+
============ =====================
|
| 282 |
+
|
| 283 |
+
Note that all sorts on statistics are in descending order (placing
|
| 284 |
+
most time consuming items first), where as name, file, and line number
|
| 285 |
+
searches are in ascending order (i.e., alphabetical). The subtle
|
| 286 |
+
distinction between "nfl" and "stdname" is that the standard name is a
|
| 287 |
+
sort of the name as printed, which means that the embedded line
|
| 288 |
+
numbers get compared in an odd way. For example, lines 3, 20, and 40
|
| 289 |
+
would (if the file names were the same) appear in the string order
|
| 290 |
+
"20" "3" and "40". In contrast, "nfl" does a numeric compare of the
|
| 291 |
+
line numbers. In fact, sort_stats("nfl") is the same as
|
| 292 |
+
sort_stats("name", "file", "line").
|
| 293 |
+
|
| 294 |
+
-T <filename>
|
| 295 |
+
save profile results as shown on screen to a text
|
| 296 |
+
file. The profile is still shown on screen.
|
| 297 |
+
|
| 298 |
+
-D <filename>
|
| 299 |
+
save (via dump_stats) profile statistics to given
|
| 300 |
+
filename. This data is in a format understood by the pstats module, and
|
| 301 |
+
is generated by a call to the dump_stats() method of profile
|
| 302 |
+
objects. The profile is still shown on screen.
|
| 303 |
+
|
| 304 |
+
-q
|
| 305 |
+
suppress output to the pager. Best used with -T and/or -D above.
|
| 306 |
+
|
| 307 |
+
If you want to run complete programs under the profiler's control, use
|
| 308 |
+
``%run -p [prof_opts] filename.py [args to program]`` where prof_opts
|
| 309 |
+
contains profiler specific options as described here.
|
| 310 |
+
|
| 311 |
+
You can read the complete documentation for the profile module with::
|
| 312 |
+
|
| 313 |
+
In [1]: import profile; profile.help()
|
| 314 |
+
|
| 315 |
+
.. versionchanged:: 7.3
|
| 316 |
+
User variables are no longer expanded,
|
| 317 |
+
the magic line is always left unmodified.
|
| 318 |
+
|
| 319 |
+
"""
|
| 320 |
+
# TODO: port to magic_arguments as currently this is duplicated in IPCompleter._extract_code
|
| 321 |
+
opts, arg_str = self.parse_options(parameter_s, 'D:l:rs:T:q',
|
| 322 |
+
list_all=True, posix=False)
|
| 323 |
+
if cell is not None:
|
| 324 |
+
arg_str += '\n' + cell
|
| 325 |
+
arg_str = self.shell.transform_cell(arg_str)
|
| 326 |
+
return self._run_with_profiler(arg_str, opts, self.shell.user_ns)
|
| 327 |
+
|
| 328 |
+
def _run_with_profiler(self, code, opts, namespace):
|
| 329 |
+
"""
|
| 330 |
+
Run `code` with profiler. Used by ``%prun`` and ``%run -p``.
|
| 331 |
+
|
| 332 |
+
Parameters
|
| 333 |
+
----------
|
| 334 |
+
code : str
|
| 335 |
+
Code to be executed.
|
| 336 |
+
opts : Struct
|
| 337 |
+
Options parsed by `self.parse_options`.
|
| 338 |
+
namespace : dict
|
| 339 |
+
A dictionary for Python namespace (e.g., `self.shell.user_ns`).
|
| 340 |
+
|
| 341 |
+
"""
|
| 342 |
+
|
| 343 |
+
# Fill default values for unspecified options:
|
| 344 |
+
opts.merge(Struct(D=[''], l=[], s=['time'], T=['']))
|
| 345 |
+
|
| 346 |
+
prof = profile.Profile()
|
| 347 |
+
try:
|
| 348 |
+
prof = prof.runctx(code, namespace, namespace)
|
| 349 |
+
sys_exit = ''
|
| 350 |
+
except SystemExit:
|
| 351 |
+
sys_exit = """*** SystemExit exception caught in code being profiled."""
|
| 352 |
+
|
| 353 |
+
stats = pstats.Stats(prof).strip_dirs().sort_stats(*opts.s)
|
| 354 |
+
|
| 355 |
+
lims = opts.l
|
| 356 |
+
if lims:
|
| 357 |
+
lims = [] # rebuild lims with ints/floats/strings
|
| 358 |
+
for lim in opts.l:
|
| 359 |
+
try:
|
| 360 |
+
lims.append(int(lim))
|
| 361 |
+
except ValueError:
|
| 362 |
+
try:
|
| 363 |
+
lims.append(float(lim))
|
| 364 |
+
except ValueError:
|
| 365 |
+
lims.append(lim)
|
| 366 |
+
|
| 367 |
+
# Trap output.
|
| 368 |
+
stdout_trap = StringIO()
|
| 369 |
+
stats_stream = stats.stream
|
| 370 |
+
try:
|
| 371 |
+
stats.stream = stdout_trap
|
| 372 |
+
stats.print_stats(*lims)
|
| 373 |
+
finally:
|
| 374 |
+
stats.stream = stats_stream
|
| 375 |
+
|
| 376 |
+
output = stdout_trap.getvalue()
|
| 377 |
+
output = output.rstrip()
|
| 378 |
+
|
| 379 |
+
if 'q' not in opts:
|
| 380 |
+
page.page(output)
|
| 381 |
+
print(sys_exit, end=' ')
|
| 382 |
+
|
| 383 |
+
dump_file = opts.D[0]
|
| 384 |
+
text_file = opts.T[0]
|
| 385 |
+
if dump_file:
|
| 386 |
+
prof.dump_stats(dump_file)
|
| 387 |
+
print(
|
| 388 |
+
f"\n*** Profile stats marshalled to file {repr(dump_file)}.{sys_exit}"
|
| 389 |
+
)
|
| 390 |
+
if text_file:
|
| 391 |
+
pfile = Path(text_file)
|
| 392 |
+
pfile.touch(exist_ok=True)
|
| 393 |
+
pfile.write_text(output, encoding="utf-8")
|
| 394 |
+
|
| 395 |
+
print(
|
| 396 |
+
f"\n*** Profile printout saved to text file {repr(text_file)}.{sys_exit}"
|
| 397 |
+
)
|
| 398 |
+
|
| 399 |
+
if 'r' in opts:
|
| 400 |
+
return stats
|
| 401 |
+
|
| 402 |
+
return None
|
| 403 |
+
|
| 404 |
+
@line_magic
|
| 405 |
+
def pdb(self, parameter_s=''):
|
| 406 |
+
"""Control the automatic calling of the pdb interactive debugger.
|
| 407 |
+
|
| 408 |
+
Call as '%pdb on', '%pdb 1', '%pdb off' or '%pdb 0'. If called without
|
| 409 |
+
argument it works as a toggle.
|
| 410 |
+
|
| 411 |
+
When an exception is triggered, IPython can optionally call the
|
| 412 |
+
interactive pdb debugger after the traceback printout. %pdb toggles
|
| 413 |
+
this feature on and off.
|
| 414 |
+
|
| 415 |
+
The initial state of this feature is set in your configuration
|
| 416 |
+
file (the option is ``InteractiveShell.pdb``).
|
| 417 |
+
|
| 418 |
+
If you want to just activate the debugger AFTER an exception has fired,
|
| 419 |
+
without having to type '%pdb on' and rerunning your code, you can use
|
| 420 |
+
the %debug magic."""
|
| 421 |
+
|
| 422 |
+
par = parameter_s.strip().lower()
|
| 423 |
+
|
| 424 |
+
if par:
|
| 425 |
+
try:
|
| 426 |
+
new_pdb = {'off':0,'0':0,'on':1,'1':1}[par]
|
| 427 |
+
except KeyError:
|
| 428 |
+
print ('Incorrect argument. Use on/1, off/0, '
|
| 429 |
+
'or nothing for a toggle.')
|
| 430 |
+
return
|
| 431 |
+
else:
|
| 432 |
+
# toggle
|
| 433 |
+
new_pdb = not self.shell.call_pdb
|
| 434 |
+
|
| 435 |
+
# set on the shell
|
| 436 |
+
self.shell.call_pdb = new_pdb
|
| 437 |
+
print('Automatic pdb calling has been turned',on_off(new_pdb))
|
| 438 |
+
|
| 439 |
+
@magic_arguments.magic_arguments()
|
| 440 |
+
@magic_arguments.argument('--breakpoint', '-b', metavar='FILE:LINE',
|
| 441 |
+
help="""
|
| 442 |
+
Set break point at LINE in FILE.
|
| 443 |
+
"""
|
| 444 |
+
)
|
| 445 |
+
@magic_arguments.kwds(
|
| 446 |
+
epilog="""
|
| 447 |
+
Any remaining arguments will be treated as code to run in the debugger.
|
| 448 |
+
"""
|
| 449 |
+
)
|
| 450 |
+
@no_var_expand
|
| 451 |
+
@line_cell_magic
|
| 452 |
+
@needs_local_scope
|
| 453 |
+
def debug(self, line="", cell=None, local_ns=None):
|
| 454 |
+
"""Activate the interactive debugger.
|
| 455 |
+
|
| 456 |
+
This magic command support two ways of activating debugger.
|
| 457 |
+
One is to activate debugger before executing code. This way, you
|
| 458 |
+
can set a break point, to step through the code from the point.
|
| 459 |
+
You can use this mode by giving statements to execute and optionally
|
| 460 |
+
a breakpoint.
|
| 461 |
+
|
| 462 |
+
The other one is to activate debugger in post-mortem mode. You can
|
| 463 |
+
activate this mode simply running %debug without any argument.
|
| 464 |
+
If an exception has just occurred, this lets you inspect its stack
|
| 465 |
+
frames interactively. Note that this will always work only on the last
|
| 466 |
+
traceback that occurred, so you must call this quickly after an
|
| 467 |
+
exception that you wish to inspect has fired, because if another one
|
| 468 |
+
occurs, it clobbers the previous one.
|
| 469 |
+
|
| 470 |
+
If you want IPython to automatically do this on every exception, see
|
| 471 |
+
the %pdb magic for more details.
|
| 472 |
+
|
| 473 |
+
.. versionchanged:: 7.3
|
| 474 |
+
When running code, user variables are no longer expanded,
|
| 475 |
+
the magic line is always left unmodified.
|
| 476 |
+
|
| 477 |
+
"""
|
| 478 |
+
args, extra = magic_arguments.parse_argstring(self.debug, line, partial=True)
|
| 479 |
+
|
| 480 |
+
if not (args.breakpoint or extra or cell):
|
| 481 |
+
self._debug_post_mortem()
|
| 482 |
+
elif not (args.breakpoint or cell):
|
| 483 |
+
# If there is no breakpoints, the line is just code to execute
|
| 484 |
+
self._debug_exec(line, None, local_ns)
|
| 485 |
+
else:
|
| 486 |
+
# Here we try to reconstruct the code from the output of
|
| 487 |
+
# parse_argstring. This might not work if the code has spaces
|
| 488 |
+
# For example this fails for `print("a b")`
|
| 489 |
+
code = " ".join(extra)
|
| 490 |
+
if cell:
|
| 491 |
+
code += "\n" + cell
|
| 492 |
+
self._debug_exec(code, args.breakpoint, local_ns)
|
| 493 |
+
|
| 494 |
+
def _debug_post_mortem(self):
|
| 495 |
+
self.shell.debugger(force=True)
|
| 496 |
+
|
| 497 |
+
def _debug_exec(self, code, breakpoint, local_ns=None):
|
| 498 |
+
if breakpoint:
|
| 499 |
+
(filename, bp_line) = breakpoint.rsplit(':', 1)
|
| 500 |
+
bp_line = int(bp_line)
|
| 501 |
+
else:
|
| 502 |
+
(filename, bp_line) = (None, None)
|
| 503 |
+
self._run_with_debugger(
|
| 504 |
+
code, self.shell.user_ns, filename, bp_line, local_ns=local_ns
|
| 505 |
+
)
|
| 506 |
+
|
| 507 |
+
@line_magic
|
| 508 |
+
def tb(self, s):
|
| 509 |
+
"""Print the last traceback.
|
| 510 |
+
|
| 511 |
+
Optionally, specify an exception reporting mode, tuning the
|
| 512 |
+
verbosity of the traceback. By default the currently-active exception
|
| 513 |
+
mode is used. See %xmode for changing exception reporting modes.
|
| 514 |
+
|
| 515 |
+
Valid modes: Plain, Context, Verbose, and Minimal.
|
| 516 |
+
"""
|
| 517 |
+
interactive_tb = self.shell.InteractiveTB
|
| 518 |
+
if s:
|
| 519 |
+
# Switch exception reporting mode for this one call.
|
| 520 |
+
# Ensure it is switched back.
|
| 521 |
+
def xmode_switch_err(name):
|
| 522 |
+
warn('Error changing %s exception modes.\n%s' %
|
| 523 |
+
(name,sys.exc_info()[1]))
|
| 524 |
+
|
| 525 |
+
new_mode = s.strip().capitalize()
|
| 526 |
+
original_mode = interactive_tb.mode
|
| 527 |
+
try:
|
| 528 |
+
try:
|
| 529 |
+
interactive_tb.set_mode(mode=new_mode)
|
| 530 |
+
except Exception:
|
| 531 |
+
xmode_switch_err('user')
|
| 532 |
+
else:
|
| 533 |
+
self.shell.showtraceback()
|
| 534 |
+
finally:
|
| 535 |
+
interactive_tb.set_mode(mode=original_mode)
|
| 536 |
+
else:
|
| 537 |
+
self.shell.showtraceback()
|
| 538 |
+
|
| 539 |
+
@skip_doctest
|
| 540 |
+
@line_magic
|
| 541 |
+
def run(self, parameter_s='', runner=None,
|
| 542 |
+
file_finder=get_py_filename):
|
| 543 |
+
"""Run the named file inside IPython as a program.
|
| 544 |
+
|
| 545 |
+
Usage::
|
| 546 |
+
|
| 547 |
+
%run [-n -i -e -G]
|
| 548 |
+
[( -t [-N<N>] | -d [-b<N>] | -p [profile options] )]
|
| 549 |
+
( -m mod | filename ) [args]
|
| 550 |
+
|
| 551 |
+
The filename argument should be either a pure Python script (with
|
| 552 |
+
extension ``.py``), or a file with custom IPython syntax (such as
|
| 553 |
+
magics). If the latter, the file can be either a script with ``.ipy``
|
| 554 |
+
extension, or a Jupyter notebook with ``.ipynb`` extension. When running
|
| 555 |
+
a Jupyter notebook, the output from print statements and other
|
| 556 |
+
displayed objects will appear in the terminal (even matplotlib figures
|
| 557 |
+
will open, if a terminal-compliant backend is being used). Note that,
|
| 558 |
+
at the system command line, the ``jupyter run`` command offers similar
|
| 559 |
+
functionality for executing notebooks (albeit currently with some
|
| 560 |
+
differences in supported options).
|
| 561 |
+
|
| 562 |
+
Parameters after the filename are passed as command-line arguments to
|
| 563 |
+
the program (put in sys.argv). Then, control returns to IPython's
|
| 564 |
+
prompt.
|
| 565 |
+
|
| 566 |
+
This is similar to running at a system prompt ``python file args``,
|
| 567 |
+
but with the advantage of giving you IPython's tracebacks, and of
|
| 568 |
+
loading all variables into your interactive namespace for further use
|
| 569 |
+
(unless -p is used, see below).
|
| 570 |
+
|
| 571 |
+
The file is executed in a namespace initially consisting only of
|
| 572 |
+
``__name__=='__main__'`` and sys.argv constructed as indicated. It thus
|
| 573 |
+
sees its environment as if it were being run as a stand-alone program
|
| 574 |
+
(except for sharing global objects such as previously imported
|
| 575 |
+
modules). But after execution, the IPython interactive namespace gets
|
| 576 |
+
updated with all variables defined in the program (except for ``__name__``
|
| 577 |
+
and ``sys.argv``). This allows for very convenient loading of code for
|
| 578 |
+
interactive work, while giving each program a 'clean sheet' to run in.
|
| 579 |
+
|
| 580 |
+
Arguments are expanded using shell-like glob match. Patterns
|
| 581 |
+
'*', '?', '[seq]' and '[!seq]' can be used. Additionally,
|
| 582 |
+
tilde '~' will be expanded into user's home directory. Unlike
|
| 583 |
+
real shells, quotation does not suppress expansions. Use
|
| 584 |
+
*two* back slashes (e.g. ``\\\\*``) to suppress expansions.
|
| 585 |
+
To completely disable these expansions, you can use -G flag.
|
| 586 |
+
|
| 587 |
+
On Windows systems, the use of single quotes `'` when specifying
|
| 588 |
+
a file is not supported. Use double quotes `"`.
|
| 589 |
+
|
| 590 |
+
Options:
|
| 591 |
+
|
| 592 |
+
-n
|
| 593 |
+
__name__ is NOT set to '__main__', but to the running file's name
|
| 594 |
+
without extension (as python does under import). This allows running
|
| 595 |
+
scripts and reloading the definitions in them without calling code
|
| 596 |
+
protected by an ``if __name__ == "__main__"`` clause.
|
| 597 |
+
|
| 598 |
+
-i
|
| 599 |
+
run the file in IPython's namespace instead of an empty one. This
|
| 600 |
+
is useful if you are experimenting with code written in a text editor
|
| 601 |
+
which depends on variables defined interactively.
|
| 602 |
+
|
| 603 |
+
-e
|
| 604 |
+
ignore sys.exit() calls or SystemExit exceptions in the script
|
| 605 |
+
being run. This is particularly useful if IPython is being used to
|
| 606 |
+
run unittests, which always exit with a sys.exit() call. In such
|
| 607 |
+
cases you are interested in the output of the test results, not in
|
| 608 |
+
seeing a traceback of the unittest module.
|
| 609 |
+
|
| 610 |
+
-t
|
| 611 |
+
print timing information at the end of the run. IPython will give
|
| 612 |
+
you an estimated CPU time consumption for your script, which under
|
| 613 |
+
Unix uses the resource module to avoid the wraparound problems of
|
| 614 |
+
time.clock(). Under Unix, an estimate of time spent on system tasks
|
| 615 |
+
is also given (for Windows platforms this is reported as 0.0).
|
| 616 |
+
|
| 617 |
+
If -t is given, an additional ``-N<N>`` option can be given, where <N>
|
| 618 |
+
must be an integer indicating how many times you want the script to
|
| 619 |
+
run. The final timing report will include total and per run results.
|
| 620 |
+
|
| 621 |
+
For example (testing the script myscript.py)::
|
| 622 |
+
|
| 623 |
+
In [1]: run -t myscript
|
| 624 |
+
|
| 625 |
+
IPython CPU timings (estimated):
|
| 626 |
+
User : 0.19597 s.
|
| 627 |
+
System: 0.0 s.
|
| 628 |
+
|
| 629 |
+
In [2]: run -t -N5 myscript
|
| 630 |
+
|
| 631 |
+
IPython CPU timings (estimated):
|
| 632 |
+
Total runs performed: 5
|
| 633 |
+
Times : Total Per run
|
| 634 |
+
User : 0.910862 s, 0.1821724 s.
|
| 635 |
+
System: 0.0 s, 0.0 s.
|
| 636 |
+
|
| 637 |
+
-d
|
| 638 |
+
run your program under the control of pdb, the Python debugger.
|
| 639 |
+
This allows you to execute your program step by step, watch variables,
|
| 640 |
+
etc. Internally, what IPython does is similar to calling::
|
| 641 |
+
|
| 642 |
+
pdb.run('execfile("YOURFILENAME")')
|
| 643 |
+
|
| 644 |
+
with a breakpoint set on line 1 of your file. You can change the line
|
| 645 |
+
number for this automatic breakpoint to be <N> by using the -bN option
|
| 646 |
+
(where N must be an integer). For example::
|
| 647 |
+
|
| 648 |
+
%run -d -b40 myscript
|
| 649 |
+
|
| 650 |
+
will set the first breakpoint at line 40 in myscript.py. Note that
|
| 651 |
+
the first breakpoint must be set on a line which actually does
|
| 652 |
+
something (not a comment or docstring) for it to stop execution.
|
| 653 |
+
|
| 654 |
+
Or you can specify a breakpoint in a different file::
|
| 655 |
+
|
| 656 |
+
%run -d -b myotherfile.py:20 myscript
|
| 657 |
+
|
| 658 |
+
When the pdb debugger starts, you will see a (Pdb) prompt. You must
|
| 659 |
+
first enter 'c' (without quotes) to start execution up to the first
|
| 660 |
+
breakpoint.
|
| 661 |
+
|
| 662 |
+
Entering 'help' gives information about the use of the debugger. You
|
| 663 |
+
can easily see pdb's full documentation with "import pdb;pdb.help()"
|
| 664 |
+
at a prompt.
|
| 665 |
+
|
| 666 |
+
-p
|
| 667 |
+
run program under the control of the Python profiler module (which
|
| 668 |
+
prints a detailed report of execution times, function calls, etc).
|
| 669 |
+
|
| 670 |
+
You can pass other options after -p which affect the behavior of the
|
| 671 |
+
profiler itself. See the docs for %prun for details.
|
| 672 |
+
|
| 673 |
+
In this mode, the program's variables do NOT propagate back to the
|
| 674 |
+
IPython interactive namespace (because they remain in the namespace
|
| 675 |
+
where the profiler executes them).
|
| 676 |
+
|
| 677 |
+
Internally this triggers a call to %prun, see its documentation for
|
| 678 |
+
details on the options available specifically for profiling.
|
| 679 |
+
|
| 680 |
+
There is one special usage for which the text above doesn't apply:
|
| 681 |
+
if the filename ends with .ipy[nb], the file is run as ipython script,
|
| 682 |
+
just as if the commands were written on IPython prompt.
|
| 683 |
+
|
| 684 |
+
-m
|
| 685 |
+
specify module name to load instead of script path. Similar to
|
| 686 |
+
the -m option for the python interpreter. Use this option last if you
|
| 687 |
+
want to combine with other %run options. Unlike the python interpreter
|
| 688 |
+
only source modules are allowed no .pyc or .pyo files.
|
| 689 |
+
For example::
|
| 690 |
+
|
| 691 |
+
%run -m example
|
| 692 |
+
|
| 693 |
+
will run the example module.
|
| 694 |
+
|
| 695 |
+
-G
|
| 696 |
+
disable shell-like glob expansion of arguments.
|
| 697 |
+
|
| 698 |
+
"""
|
| 699 |
+
|
| 700 |
+
# Logic to handle issue #3664
|
| 701 |
+
# Add '--' after '-m <module_name>' to ignore additional args passed to a module.
|
| 702 |
+
if '-m' in parameter_s and '--' not in parameter_s:
|
| 703 |
+
argv = shlex.split(parameter_s, posix=(os.name == 'posix'))
|
| 704 |
+
for idx, arg in enumerate(argv):
|
| 705 |
+
if arg and arg.startswith('-') and arg != '-':
|
| 706 |
+
if arg == '-m':
|
| 707 |
+
argv.insert(idx + 2, '--')
|
| 708 |
+
break
|
| 709 |
+
else:
|
| 710 |
+
# Positional arg, break
|
| 711 |
+
break
|
| 712 |
+
parameter_s = ' '.join(shlex.quote(arg) for arg in argv)
|
| 713 |
+
|
| 714 |
+
# get arguments and set sys.argv for program to be run.
|
| 715 |
+
opts, arg_lst = self.parse_options(parameter_s,
|
| 716 |
+
'nidtN:b:pD:l:rs:T:em:G',
|
| 717 |
+
mode='list', list_all=1)
|
| 718 |
+
if "m" in opts:
|
| 719 |
+
modulename = opts["m"][0]
|
| 720 |
+
modpath = find_mod(modulename)
|
| 721 |
+
if modpath is None:
|
| 722 |
+
msg = '%r is not a valid modulename on sys.path'%modulename
|
| 723 |
+
raise Exception(msg)
|
| 724 |
+
arg_lst = [modpath] + arg_lst
|
| 725 |
+
try:
|
| 726 |
+
fpath = None # initialize to make sure fpath is in scope later
|
| 727 |
+
fpath = arg_lst[0]
|
| 728 |
+
filename = file_finder(fpath)
|
| 729 |
+
except IndexError as e:
|
| 730 |
+
msg = 'you must provide at least a filename.'
|
| 731 |
+
raise Exception(msg) from e
|
| 732 |
+
except IOError as e:
|
| 733 |
+
try:
|
| 734 |
+
msg = str(e)
|
| 735 |
+
except UnicodeError:
|
| 736 |
+
msg = e.message
|
| 737 |
+
if os.name == 'nt' and re.match(r"^'.*'$",fpath):
|
| 738 |
+
warn('For Windows, use double quotes to wrap a filename: %run "mypath\\myfile.py"')
|
| 739 |
+
raise Exception(msg) from e
|
| 740 |
+
except TypeError:
|
| 741 |
+
if fpath in sys.meta_path:
|
| 742 |
+
filename = ""
|
| 743 |
+
else:
|
| 744 |
+
raise
|
| 745 |
+
|
| 746 |
+
if filename.lower().endswith(('.ipy', '.ipynb')):
|
| 747 |
+
with preserve_keys(self.shell.user_ns, '__file__'):
|
| 748 |
+
self.shell.user_ns['__file__'] = filename
|
| 749 |
+
self.shell.safe_execfile_ipy(filename, raise_exceptions=True)
|
| 750 |
+
return
|
| 751 |
+
|
| 752 |
+
# Control the response to exit() calls made by the script being run
|
| 753 |
+
exit_ignore = 'e' in opts
|
| 754 |
+
|
| 755 |
+
# Make sure that the running script gets a proper sys.argv as if it
|
| 756 |
+
# were run from a system shell.
|
| 757 |
+
save_argv = sys.argv # save it for later restoring
|
| 758 |
+
|
| 759 |
+
if 'G' in opts:
|
| 760 |
+
args = arg_lst[1:]
|
| 761 |
+
else:
|
| 762 |
+
# tilde and glob expansion
|
| 763 |
+
args = shellglob(map(os.path.expanduser, arg_lst[1:]))
|
| 764 |
+
|
| 765 |
+
sys.argv = [filename] + args # put in the proper filename
|
| 766 |
+
|
| 767 |
+
if 'n' in opts:
|
| 768 |
+
name = Path(filename).stem
|
| 769 |
+
else:
|
| 770 |
+
name = '__main__'
|
| 771 |
+
|
| 772 |
+
if 'i' in opts:
|
| 773 |
+
# Run in user's interactive namespace
|
| 774 |
+
prog_ns = self.shell.user_ns
|
| 775 |
+
__name__save = self.shell.user_ns['__name__']
|
| 776 |
+
prog_ns['__name__'] = name
|
| 777 |
+
main_mod = self.shell.user_module
|
| 778 |
+
|
| 779 |
+
# Since '%run foo' emulates 'python foo.py' at the cmd line, we must
|
| 780 |
+
# set the __file__ global in the script's namespace
|
| 781 |
+
# TK: Is this necessary in interactive mode?
|
| 782 |
+
prog_ns['__file__'] = filename
|
| 783 |
+
else:
|
| 784 |
+
# Run in a fresh, empty namespace
|
| 785 |
+
|
| 786 |
+
# The shell MUST hold a reference to prog_ns so after %run
|
| 787 |
+
# exits, the python deletion mechanism doesn't zero it out
|
| 788 |
+
# (leaving dangling references). See interactiveshell for details
|
| 789 |
+
main_mod = self.shell.new_main_mod(filename, name)
|
| 790 |
+
prog_ns = main_mod.__dict__
|
| 791 |
+
|
| 792 |
+
# pickle fix. See interactiveshell for an explanation. But we need to
|
| 793 |
+
# make sure that, if we overwrite __main__, we replace it at the end
|
| 794 |
+
main_mod_name = prog_ns['__name__']
|
| 795 |
+
|
| 796 |
+
if main_mod_name == '__main__':
|
| 797 |
+
restore_main = sys.modules['__main__']
|
| 798 |
+
else:
|
| 799 |
+
restore_main = False
|
| 800 |
+
|
| 801 |
+
# This needs to be undone at the end to prevent holding references to
|
| 802 |
+
# every single object ever created.
|
| 803 |
+
sys.modules[main_mod_name] = main_mod
|
| 804 |
+
|
| 805 |
+
if 'p' in opts or 'd' in opts:
|
| 806 |
+
if 'm' in opts:
|
| 807 |
+
code = 'run_module(modulename, prog_ns)'
|
| 808 |
+
code_ns = {
|
| 809 |
+
'run_module': self.shell.safe_run_module,
|
| 810 |
+
'prog_ns': prog_ns,
|
| 811 |
+
'modulename': modulename,
|
| 812 |
+
}
|
| 813 |
+
else:
|
| 814 |
+
if 'd' in opts:
|
| 815 |
+
# allow exceptions to raise in debug mode
|
| 816 |
+
code = 'execfile(filename, prog_ns, raise_exceptions=True)'
|
| 817 |
+
else:
|
| 818 |
+
code = 'execfile(filename, prog_ns)'
|
| 819 |
+
code_ns = {
|
| 820 |
+
'execfile': self.shell.safe_execfile,
|
| 821 |
+
'prog_ns': prog_ns,
|
| 822 |
+
'filename': get_py_filename(filename),
|
| 823 |
+
}
|
| 824 |
+
|
| 825 |
+
try:
|
| 826 |
+
stats = None
|
| 827 |
+
if 'p' in opts:
|
| 828 |
+
stats = self._run_with_profiler(code, opts, code_ns)
|
| 829 |
+
else:
|
| 830 |
+
if 'd' in opts:
|
| 831 |
+
bp_file, bp_line = parse_breakpoint(
|
| 832 |
+
opts.get('b', ['1'])[0], filename)
|
| 833 |
+
self._run_with_debugger(
|
| 834 |
+
code, code_ns, filename, bp_line, bp_file)
|
| 835 |
+
else:
|
| 836 |
+
if 'm' in opts:
|
| 837 |
+
def run():
|
| 838 |
+
self.shell.safe_run_module(modulename, prog_ns)
|
| 839 |
+
else:
|
| 840 |
+
if runner is None:
|
| 841 |
+
runner = self.default_runner
|
| 842 |
+
if runner is None:
|
| 843 |
+
runner = self.shell.safe_execfile
|
| 844 |
+
|
| 845 |
+
def run():
|
| 846 |
+
runner(filename, prog_ns, prog_ns,
|
| 847 |
+
exit_ignore=exit_ignore)
|
| 848 |
+
|
| 849 |
+
if 't' in opts:
|
| 850 |
+
# timed execution
|
| 851 |
+
try:
|
| 852 |
+
nruns = int(opts['N'][0])
|
| 853 |
+
if nruns < 1:
|
| 854 |
+
error('Number of runs must be >=1')
|
| 855 |
+
return
|
| 856 |
+
except (KeyError):
|
| 857 |
+
nruns = 1
|
| 858 |
+
self._run_with_timing(run, nruns)
|
| 859 |
+
else:
|
| 860 |
+
# regular execution
|
| 861 |
+
run()
|
| 862 |
+
|
| 863 |
+
if 'i' in opts:
|
| 864 |
+
self.shell.user_ns['__name__'] = __name__save
|
| 865 |
+
else:
|
| 866 |
+
# update IPython interactive namespace
|
| 867 |
+
|
| 868 |
+
# Some forms of read errors on the file may mean the
|
| 869 |
+
# __name__ key was never set; using pop we don't have to
|
| 870 |
+
# worry about a possible KeyError.
|
| 871 |
+
prog_ns.pop('__name__', None)
|
| 872 |
+
|
| 873 |
+
with preserve_keys(self.shell.user_ns, '__file__'):
|
| 874 |
+
self.shell.user_ns.update(prog_ns)
|
| 875 |
+
finally:
|
| 876 |
+
# It's a bit of a mystery why, but __builtins__ can change from
|
| 877 |
+
# being a module to becoming a dict missing some key data after
|
| 878 |
+
# %run. As best I can see, this is NOT something IPython is doing
|
| 879 |
+
# at all, and similar problems have been reported before:
|
| 880 |
+
# http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html
|
| 881 |
+
# Since this seems to be done by the interpreter itself, the best
|
| 882 |
+
# we can do is to at least restore __builtins__ for the user on
|
| 883 |
+
# exit.
|
| 884 |
+
self.shell.user_ns['__builtins__'] = builtin_mod
|
| 885 |
+
|
| 886 |
+
# Ensure key global structures are restored
|
| 887 |
+
sys.argv = save_argv
|
| 888 |
+
if restore_main:
|
| 889 |
+
sys.modules['__main__'] = restore_main
|
| 890 |
+
if '__mp_main__' in sys.modules:
|
| 891 |
+
sys.modules['__mp_main__'] = restore_main
|
| 892 |
+
else:
|
| 893 |
+
# Remove from sys.modules the reference to main_mod we'd
|
| 894 |
+
# added. Otherwise it will trap references to objects
|
| 895 |
+
# contained therein.
|
| 896 |
+
del sys.modules[main_mod_name]
|
| 897 |
+
|
| 898 |
+
return stats
|
| 899 |
+
|
| 900 |
+
def _run_with_debugger(
|
| 901 |
+
self, code, code_ns, filename=None, bp_line=None, bp_file=None, local_ns=None
|
| 902 |
+
):
|
| 903 |
+
"""
|
| 904 |
+
Run `code` in debugger with a break point.
|
| 905 |
+
|
| 906 |
+
Parameters
|
| 907 |
+
----------
|
| 908 |
+
code : str
|
| 909 |
+
Code to execute.
|
| 910 |
+
code_ns : dict
|
| 911 |
+
A namespace in which `code` is executed.
|
| 912 |
+
filename : str
|
| 913 |
+
`code` is ran as if it is in `filename`.
|
| 914 |
+
bp_line : int, optional
|
| 915 |
+
Line number of the break point.
|
| 916 |
+
bp_file : str, optional
|
| 917 |
+
Path to the file in which break point is specified.
|
| 918 |
+
`filename` is used if not given.
|
| 919 |
+
local_ns : dict, optional
|
| 920 |
+
A local namespace in which `code` is executed.
|
| 921 |
+
|
| 922 |
+
Raises
|
| 923 |
+
------
|
| 924 |
+
UsageError
|
| 925 |
+
If the break point given by `bp_line` is not valid.
|
| 926 |
+
|
| 927 |
+
"""
|
| 928 |
+
deb = self.shell.InteractiveTB.pdb
|
| 929 |
+
if not deb:
|
| 930 |
+
self.shell.InteractiveTB.pdb = self.shell.InteractiveTB.debugger_cls()
|
| 931 |
+
deb = self.shell.InteractiveTB.pdb
|
| 932 |
+
|
| 933 |
+
# reset Breakpoint state, which is moronically kept
|
| 934 |
+
# in a class
|
| 935 |
+
bdb.Breakpoint.next = 1
|
| 936 |
+
bdb.Breakpoint.bplist = {}
|
| 937 |
+
bdb.Breakpoint.bpbynumber = [None]
|
| 938 |
+
deb.clear_all_breaks()
|
| 939 |
+
if bp_line is not None:
|
| 940 |
+
# Set an initial breakpoint to stop execution
|
| 941 |
+
maxtries = 10
|
| 942 |
+
bp_file = bp_file or filename
|
| 943 |
+
checkline = deb.checkline(bp_file, bp_line)
|
| 944 |
+
if not checkline:
|
| 945 |
+
for bp in range(bp_line + 1, bp_line + maxtries + 1):
|
| 946 |
+
if deb.checkline(bp_file, bp):
|
| 947 |
+
break
|
| 948 |
+
else:
|
| 949 |
+
msg = ("\nI failed to find a valid line to set "
|
| 950 |
+
"a breakpoint\n"
|
| 951 |
+
"after trying up to line: %s.\n"
|
| 952 |
+
"Please set a valid breakpoint manually "
|
| 953 |
+
"with the -b option." % bp)
|
| 954 |
+
raise UsageError(msg)
|
| 955 |
+
# if we find a good linenumber, set the breakpoint
|
| 956 |
+
deb.do_break('%s:%s' % (bp_file, bp_line))
|
| 957 |
+
|
| 958 |
+
if filename:
|
| 959 |
+
# Mimic Pdb._runscript(...)
|
| 960 |
+
deb._wait_for_mainpyfile = True
|
| 961 |
+
deb.mainpyfile = deb.canonic(filename)
|
| 962 |
+
|
| 963 |
+
# Start file run
|
| 964 |
+
print("NOTE: Enter 'c' at the %s prompt to continue execution." % deb.prompt)
|
| 965 |
+
try:
|
| 966 |
+
if filename:
|
| 967 |
+
# save filename so it can be used by methods on the deb object
|
| 968 |
+
deb._exec_filename = filename
|
| 969 |
+
while True:
|
| 970 |
+
try:
|
| 971 |
+
trace = sys.gettrace()
|
| 972 |
+
deb.run(code, code_ns, local_ns)
|
| 973 |
+
except Restart:
|
| 974 |
+
print("Restarting")
|
| 975 |
+
if filename:
|
| 976 |
+
deb._wait_for_mainpyfile = True
|
| 977 |
+
deb.mainpyfile = deb.canonic(filename)
|
| 978 |
+
continue
|
| 979 |
+
else:
|
| 980 |
+
break
|
| 981 |
+
finally:
|
| 982 |
+
sys.settrace(trace)
|
| 983 |
+
|
| 984 |
+
# Perform proper cleanup of the session in case if
|
| 985 |
+
# it exited with "continue" and not "quit" command
|
| 986 |
+
if hasattr(deb, "rcLines"):
|
| 987 |
+
# Run this code defensively in case if custom debugger
|
| 988 |
+
# class does not implement rcLines, which although public
|
| 989 |
+
# is an implementation detail of `pdb.Pdb` and not part of
|
| 990 |
+
# the more generic basic debugger framework (`bdb.Bdb`).
|
| 991 |
+
deb.set_quit()
|
| 992 |
+
deb.rcLines.extend(["q"])
|
| 993 |
+
try:
|
| 994 |
+
deb.run("", code_ns, local_ns)
|
| 995 |
+
except StopIteration:
|
| 996 |
+
# Stop iteration is raised on quit command
|
| 997 |
+
pass
|
| 998 |
+
|
| 999 |
+
except Exception:
|
| 1000 |
+
etype, value, tb = sys.exc_info()
|
| 1001 |
+
# Skip three frames in the traceback: the %run one,
|
| 1002 |
+
# one inside bdb.py, and the command-line typed by the
|
| 1003 |
+
# user (run by exec in pdb itself).
|
| 1004 |
+
self.shell.InteractiveTB(etype, value, tb, tb_offset=3)
|
| 1005 |
+
|
| 1006 |
+
@staticmethod
|
| 1007 |
+
def _run_with_timing(run, nruns):
|
| 1008 |
+
"""
|
| 1009 |
+
Run function `run` and print timing information.
|
| 1010 |
+
|
| 1011 |
+
Parameters
|
| 1012 |
+
----------
|
| 1013 |
+
run : callable
|
| 1014 |
+
Any callable object which takes no argument.
|
| 1015 |
+
nruns : int
|
| 1016 |
+
Number of times to execute `run`.
|
| 1017 |
+
|
| 1018 |
+
"""
|
| 1019 |
+
twall0 = time.perf_counter()
|
| 1020 |
+
if nruns == 1:
|
| 1021 |
+
t0 = clock2()
|
| 1022 |
+
run()
|
| 1023 |
+
t1 = clock2()
|
| 1024 |
+
t_usr = t1[0] - t0[0]
|
| 1025 |
+
t_sys = t1[1] - t0[1]
|
| 1026 |
+
print("\nIPython CPU timings (estimated):")
|
| 1027 |
+
print(" User : %10.2f s." % t_usr)
|
| 1028 |
+
print(" System : %10.2f s." % t_sys)
|
| 1029 |
+
else:
|
| 1030 |
+
runs = range(nruns)
|
| 1031 |
+
t0 = clock2()
|
| 1032 |
+
for nr in runs:
|
| 1033 |
+
run()
|
| 1034 |
+
t1 = clock2()
|
| 1035 |
+
t_usr = t1[0] - t0[0]
|
| 1036 |
+
t_sys = t1[1] - t0[1]
|
| 1037 |
+
print("\nIPython CPU timings (estimated):")
|
| 1038 |
+
print("Total runs performed:", nruns)
|
| 1039 |
+
print(" Times : %10s %10s" % ('Total', 'Per run'))
|
| 1040 |
+
print(" User : %10.2f s, %10.2f s." % (t_usr, t_usr / nruns))
|
| 1041 |
+
print(" System : %10.2f s, %10.2f s." % (t_sys, t_sys / nruns))
|
| 1042 |
+
twall1 = time.perf_counter()
|
| 1043 |
+
print("Wall time: %10.2f s." % (twall1 - twall0))
|
| 1044 |
+
|
| 1045 |
+
@skip_doctest
|
| 1046 |
+
@no_var_expand
|
| 1047 |
+
@line_cell_magic
|
| 1048 |
+
@needs_local_scope
|
| 1049 |
+
def timeit(self, line='', cell=None, local_ns=None):
|
| 1050 |
+
"""Time execution of a Python statement or expression
|
| 1051 |
+
|
| 1052 |
+
**Usage, in line mode**::
|
| 1053 |
+
|
| 1054 |
+
%timeit [-n<N> -r<R> [-t|-c] -q -p<P> [-o|-v <V>]] statement
|
| 1055 |
+
|
| 1056 |
+
**or in cell mode**::
|
| 1057 |
+
|
| 1058 |
+
%%timeit [-n<N> -r<R> [-t|-c] -q -p<P> [-o|-v <V>]] setup_code
|
| 1059 |
+
code
|
| 1060 |
+
code...
|
| 1061 |
+
|
| 1062 |
+
Time execution of a Python statement or expression using the timeit
|
| 1063 |
+
module. This function can be used both as a line and cell magic:
|
| 1064 |
+
|
| 1065 |
+
- In line mode you can time a single-line statement (though multiple
|
| 1066 |
+
ones can be chained with using semicolons).
|
| 1067 |
+
|
| 1068 |
+
- In cell mode, the statement in the first line is used as setup code
|
| 1069 |
+
(executed but not timed) and the body of the cell is timed. The cell
|
| 1070 |
+
body has access to any variables created in the setup code.
|
| 1071 |
+
|
| 1072 |
+
Options:
|
| 1073 |
+
|
| 1074 |
+
-n<N>
|
| 1075 |
+
Execute the given statement N times in a loop. If N is not
|
| 1076 |
+
provided, N is determined so as to get sufficient accuracy.
|
| 1077 |
+
|
| 1078 |
+
-r<R>
|
| 1079 |
+
Number of repeats R, each consisting of N loops, and take the
|
| 1080 |
+
average result.
|
| 1081 |
+
Default: 7
|
| 1082 |
+
|
| 1083 |
+
-t
|
| 1084 |
+
Use ``time.time`` to measure the time, which is the default on Unix.
|
| 1085 |
+
This function measures wall time.
|
| 1086 |
+
|
| 1087 |
+
-c
|
| 1088 |
+
Use ``time.clock`` to measure the time, which is the default on
|
| 1089 |
+
Windows and measures wall time. On Unix, ``resource.getrusage`` is used
|
| 1090 |
+
instead and returns the CPU user time.
|
| 1091 |
+
|
| 1092 |
+
-p<P>
|
| 1093 |
+
Use a precision of P digits to display the timing result.
|
| 1094 |
+
Default: 3
|
| 1095 |
+
|
| 1096 |
+
-q
|
| 1097 |
+
Quiet, do not print result.
|
| 1098 |
+
|
| 1099 |
+
-o
|
| 1100 |
+
Return a ``TimeitResult`` that can be stored in a variable to inspect
|
| 1101 |
+
the result in more details.
|
| 1102 |
+
|
| 1103 |
+
-v <V>
|
| 1104 |
+
Like ``-o``, but save the ``TimeitResult`` directly to variable <V>.
|
| 1105 |
+
|
| 1106 |
+
.. versionchanged:: 7.3
|
| 1107 |
+
User variables are no longer expanded,
|
| 1108 |
+
the magic line is always left unmodified.
|
| 1109 |
+
|
| 1110 |
+
Examples
|
| 1111 |
+
--------
|
| 1112 |
+
::
|
| 1113 |
+
|
| 1114 |
+
In [1]: %timeit pass
|
| 1115 |
+
8.26 ns ± 0.12 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)
|
| 1116 |
+
|
| 1117 |
+
In [2]: u = None
|
| 1118 |
+
|
| 1119 |
+
In [3]: %timeit u is None
|
| 1120 |
+
29.9 ns ± 0.643 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
|
| 1121 |
+
|
| 1122 |
+
In [4]: %timeit -r 4 u == None
|
| 1123 |
+
|
| 1124 |
+
In [5]: import time
|
| 1125 |
+
|
| 1126 |
+
In [6]: %timeit -n1 time.sleep(2)
|
| 1127 |
+
|
| 1128 |
+
The times reported by ``%timeit`` will be slightly higher than those
|
| 1129 |
+
reported by the timeit.py script when variables are accessed. This is
|
| 1130 |
+
due to the fact that ``%timeit`` executes the statement in the namespace
|
| 1131 |
+
of the shell, compared with timeit.py, which uses a single setup
|
| 1132 |
+
statement to import function or create variables. Generally, the bias
|
| 1133 |
+
does not matter as long as results from timeit.py are not mixed with
|
| 1134 |
+
those from ``%timeit``."""
|
| 1135 |
+
|
| 1136 |
+
# TODO: port to magic_arguments as currently this is duplicated in IPCompleter._extract_code
|
| 1137 |
+
opts, stmt = self.parse_options(
|
| 1138 |
+
line, "n:r:tcp:qov:", posix=False, strict=False, preserve_non_opts=True
|
| 1139 |
+
)
|
| 1140 |
+
if stmt == "" and cell is None:
|
| 1141 |
+
return
|
| 1142 |
+
|
| 1143 |
+
timefunc = timeit.default_timer
|
| 1144 |
+
number = int(getattr(opts, "n", 0))
|
| 1145 |
+
default_repeat = 7 if timeit.default_repeat < 7 else timeit.default_repeat
|
| 1146 |
+
repeat = int(getattr(opts, "r", default_repeat))
|
| 1147 |
+
precision = int(getattr(opts, "p", 3))
|
| 1148 |
+
quiet = "q" in opts
|
| 1149 |
+
return_result = "o" in opts
|
| 1150 |
+
save_result = "v" in opts
|
| 1151 |
+
if hasattr(opts, "t"):
|
| 1152 |
+
timefunc = time.time
|
| 1153 |
+
if hasattr(opts, "c"):
|
| 1154 |
+
timefunc = clock
|
| 1155 |
+
|
| 1156 |
+
timer = Timer(timer=timefunc)
|
| 1157 |
+
# this code has tight coupling to the inner workings of timeit.Timer,
|
| 1158 |
+
# but is there a better way to achieve that the code stmt has access
|
| 1159 |
+
# to the shell namespace?
|
| 1160 |
+
transform = self.shell.transform_cell
|
| 1161 |
+
|
| 1162 |
+
if cell is None:
|
| 1163 |
+
# called as line magic
|
| 1164 |
+
ast_setup = self.shell.compile.ast_parse("pass")
|
| 1165 |
+
ast_stmt = self.shell.compile.ast_parse(transform(stmt))
|
| 1166 |
+
else:
|
| 1167 |
+
ast_setup = self.shell.compile.ast_parse(transform(stmt))
|
| 1168 |
+
ast_stmt = self.shell.compile.ast_parse(transform(cell))
|
| 1169 |
+
|
| 1170 |
+
ast_setup = self.shell.transform_ast(ast_setup)
|
| 1171 |
+
ast_stmt = self.shell.transform_ast(ast_stmt)
|
| 1172 |
+
|
| 1173 |
+
# Check that these compile to valid Python code *outside* the timer func
|
| 1174 |
+
# Invalid code may become valid when put inside the function & loop,
|
| 1175 |
+
# which messes up error messages.
|
| 1176 |
+
# https://github.com/ipython/ipython/issues/10636
|
| 1177 |
+
self.shell.compile(ast_setup, "<magic-timeit-setup>", "exec")
|
| 1178 |
+
self.shell.compile(ast_stmt, "<magic-timeit-stmt>", "exec")
|
| 1179 |
+
|
| 1180 |
+
# This codestring is taken from timeit.template - we fill it in as an
|
| 1181 |
+
# AST, so that we can apply our AST transformations to the user code
|
| 1182 |
+
# without affecting the timing code.
|
| 1183 |
+
timeit_ast_template = ast.parse('def inner(_it, _timer):\n'
|
| 1184 |
+
' setup\n'
|
| 1185 |
+
' _t0 = _timer()\n'
|
| 1186 |
+
' for _i in _it:\n'
|
| 1187 |
+
' stmt\n'
|
| 1188 |
+
' _t1 = _timer()\n'
|
| 1189 |
+
' return _t1 - _t0\n')
|
| 1190 |
+
|
| 1191 |
+
timeit_ast = TimeitTemplateFiller(ast_setup, ast_stmt).visit(timeit_ast_template)
|
| 1192 |
+
timeit_ast = ast.fix_missing_locations(timeit_ast)
|
| 1193 |
+
|
| 1194 |
+
# Track compilation time so it can be reported if too long
|
| 1195 |
+
# Minimum time above which compilation time will be reported
|
| 1196 |
+
tc_min = 0.1
|
| 1197 |
+
|
| 1198 |
+
t0 = clock()
|
| 1199 |
+
code = self.shell.compile(timeit_ast, "<magic-timeit>", "exec")
|
| 1200 |
+
tc = clock()-t0
|
| 1201 |
+
|
| 1202 |
+
ns = {}
|
| 1203 |
+
glob = self.shell.user_ns
|
| 1204 |
+
# handles global vars with same name as local vars. We store them in conflict_globs.
|
| 1205 |
+
conflict_globs = {}
|
| 1206 |
+
if local_ns and cell is None:
|
| 1207 |
+
for var_name, var_val in glob.items():
|
| 1208 |
+
if var_name in local_ns:
|
| 1209 |
+
conflict_globs[var_name] = var_val
|
| 1210 |
+
glob.update(local_ns)
|
| 1211 |
+
|
| 1212 |
+
exec(code, glob, ns)
|
| 1213 |
+
timer.inner = ns["inner"]
|
| 1214 |
+
|
| 1215 |
+
# This is used to check if there is a huge difference between the
|
| 1216 |
+
# best and worst timings.
|
| 1217 |
+
# Issue: https://github.com/ipython/ipython/issues/6471
|
| 1218 |
+
if number == 0:
|
| 1219 |
+
# determine number so that 0.2 <= total time < 2.0
|
| 1220 |
+
for index in range(0, 10):
|
| 1221 |
+
number = 10 ** index
|
| 1222 |
+
time_number = timer.timeit(number)
|
| 1223 |
+
if time_number >= 0.2:
|
| 1224 |
+
break
|
| 1225 |
+
|
| 1226 |
+
all_runs = timer.repeat(repeat, number)
|
| 1227 |
+
best = min(all_runs) / number
|
| 1228 |
+
worst = max(all_runs) / number
|
| 1229 |
+
timeit_result = TimeitResult(number, repeat, best, worst, all_runs, tc, precision)
|
| 1230 |
+
|
| 1231 |
+
# Restore global vars from conflict_globs
|
| 1232 |
+
if conflict_globs:
|
| 1233 |
+
glob.update(conflict_globs)
|
| 1234 |
+
|
| 1235 |
+
if not quiet:
|
| 1236 |
+
# Check best timing is greater than zero to avoid a
|
| 1237 |
+
# ZeroDivisionError.
|
| 1238 |
+
# In cases where the slowest timing is lesser than a microsecond
|
| 1239 |
+
# we assume that it does not really matter if the fastest
|
| 1240 |
+
# timing is 4 times faster than the slowest timing or not.
|
| 1241 |
+
if worst > 4 * best and best > 0 and worst > 1e-6:
|
| 1242 |
+
print("The slowest run took %0.2f times longer than the "
|
| 1243 |
+
"fastest. This could mean that an intermediate result "
|
| 1244 |
+
"is being cached." % (worst / best))
|
| 1245 |
+
|
| 1246 |
+
print( timeit_result )
|
| 1247 |
+
|
| 1248 |
+
if tc > tc_min:
|
| 1249 |
+
print("Compiler time: %.2f s" % tc)
|
| 1250 |
+
|
| 1251 |
+
if save_result:
|
| 1252 |
+
self.shell.user_ns[opts.v] = timeit_result
|
| 1253 |
+
|
| 1254 |
+
if return_result:
|
| 1255 |
+
return timeit_result
|
| 1256 |
+
|
| 1257 |
+
@no_var_expand
|
| 1258 |
+
@magic_arguments.magic_arguments()
|
| 1259 |
+
@magic_arguments.argument(
|
| 1260 |
+
"--no-raise-error",
|
| 1261 |
+
action="store_true",
|
| 1262 |
+
dest="no_raise_error",
|
| 1263 |
+
help="If given, don't re-raise exceptions",
|
| 1264 |
+
)
|
| 1265 |
+
@magic_arguments.kwds(
|
| 1266 |
+
epilog="""
|
| 1267 |
+
Any remaining arguments will be treated as code to run.
|
| 1268 |
+
"""
|
| 1269 |
+
)
|
| 1270 |
+
@skip_doctest
|
| 1271 |
+
@needs_local_scope
|
| 1272 |
+
@line_cell_magic
|
| 1273 |
+
@output_can_be_silenced
|
| 1274 |
+
def time(self, line="", cell=None, local_ns=None):
|
| 1275 |
+
"""Time execution of a Python statement or expression.
|
| 1276 |
+
|
| 1277 |
+
The CPU and wall clock times are printed, and the value of the
|
| 1278 |
+
expression (if any) is returned. Note that under Win32, system time
|
| 1279 |
+
is always reported as 0, since it can not be measured.
|
| 1280 |
+
|
| 1281 |
+
This function can be used both as a line and cell magic:
|
| 1282 |
+
|
| 1283 |
+
- In line mode you can time a single-line statement (though multiple
|
| 1284 |
+
ones can be chained with using semicolons).
|
| 1285 |
+
|
| 1286 |
+
- In cell mode, you can time the cell body (a directly
|
| 1287 |
+
following statement raises an error).
|
| 1288 |
+
|
| 1289 |
+
This function provides very basic timing functionality. Use the timeit
|
| 1290 |
+
magic for more control over the measurement.
|
| 1291 |
+
|
| 1292 |
+
.. versionchanged:: 7.3
|
| 1293 |
+
User variables are no longer expanded,
|
| 1294 |
+
the magic line is always left unmodified.
|
| 1295 |
+
|
| 1296 |
+
.. versionchanged:: 8.3
|
| 1297 |
+
The time magic now correctly propagates system-exiting exceptions
|
| 1298 |
+
(such as ``KeyboardInterrupt`` invoked when interrupting execution)
|
| 1299 |
+
rather than just printing out the exception traceback.
|
| 1300 |
+
The non-system-exception will still be caught as before.
|
| 1301 |
+
|
| 1302 |
+
Examples
|
| 1303 |
+
--------
|
| 1304 |
+
::
|
| 1305 |
+
|
| 1306 |
+
In [1]: %time 2**128
|
| 1307 |
+
CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
|
| 1308 |
+
Wall time: 0.00
|
| 1309 |
+
Out[1]: 340282366920938463463374607431768211456L
|
| 1310 |
+
|
| 1311 |
+
In [2]: n = 1000000
|
| 1312 |
+
|
| 1313 |
+
In [3]: %time sum(range(n))
|
| 1314 |
+
CPU times: user 1.20 s, sys: 0.05 s, total: 1.25 s
|
| 1315 |
+
Wall time: 1.37
|
| 1316 |
+
Out[3]: 499999500000L
|
| 1317 |
+
|
| 1318 |
+
In [4]: %time print('hello world')
|
| 1319 |
+
hello world
|
| 1320 |
+
CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
|
| 1321 |
+
Wall time: 0.00
|
| 1322 |
+
|
| 1323 |
+
.. note::
|
| 1324 |
+
The time needed by Python to compile the given expression will be
|
| 1325 |
+
reported if it is more than 0.1s.
|
| 1326 |
+
|
| 1327 |
+
In the example below, the actual exponentiation is done by Python
|
| 1328 |
+
at compilation time, so while the expression can take a noticeable
|
| 1329 |
+
amount of time to compute, that time is purely due to the
|
| 1330 |
+
compilation::
|
| 1331 |
+
|
| 1332 |
+
In [5]: %time 3**9999;
|
| 1333 |
+
CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
|
| 1334 |
+
Wall time: 0.00 s
|
| 1335 |
+
|
| 1336 |
+
In [6]: %time 3**999999;
|
| 1337 |
+
CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
|
| 1338 |
+
Wall time: 0.00 s
|
| 1339 |
+
Compiler : 0.78 s
|
| 1340 |
+
"""
|
| 1341 |
+
args, extra = magic_arguments.parse_argstring(self.time, line, partial=True)
|
| 1342 |
+
line = " ".join(extra)
|
| 1343 |
+
|
| 1344 |
+
if line and cell:
|
| 1345 |
+
raise UsageError("Can't use statement directly after '%%time'!")
|
| 1346 |
+
|
| 1347 |
+
if cell:
|
| 1348 |
+
expr = self.shell.transform_cell(cell)
|
| 1349 |
+
else:
|
| 1350 |
+
expr = self.shell.transform_cell(line)
|
| 1351 |
+
|
| 1352 |
+
# Minimum time above which parse time will be reported
|
| 1353 |
+
tp_min = 0.1
|
| 1354 |
+
|
| 1355 |
+
t0 = clock()
|
| 1356 |
+
expr_ast = self.shell.compile.ast_parse(expr)
|
| 1357 |
+
tp = clock() - t0
|
| 1358 |
+
|
| 1359 |
+
# Apply AST transformations
|
| 1360 |
+
expr_ast = self.shell.transform_ast(expr_ast)
|
| 1361 |
+
|
| 1362 |
+
# Minimum time above which compilation time will be reported
|
| 1363 |
+
tc_min = 0.1
|
| 1364 |
+
|
| 1365 |
+
expr_val = None
|
| 1366 |
+
if len(expr_ast.body) == 1 and isinstance(expr_ast.body[0], ast.Expr):
|
| 1367 |
+
mode = 'eval'
|
| 1368 |
+
source = '<timed eval>'
|
| 1369 |
+
expr_ast = ast.Expression(expr_ast.body[0].value)
|
| 1370 |
+
else:
|
| 1371 |
+
mode = 'exec'
|
| 1372 |
+
source = '<timed exec>'
|
| 1373 |
+
# multi-line %%time case
|
| 1374 |
+
if len(expr_ast.body) > 1 and isinstance(expr_ast.body[-1], ast.Expr):
|
| 1375 |
+
expr_val = expr_ast.body[-1]
|
| 1376 |
+
expr_ast = expr_ast.body[:-1]
|
| 1377 |
+
expr_ast = Module(expr_ast, [])
|
| 1378 |
+
expr_val = ast.Expression(expr_val.value)
|
| 1379 |
+
|
| 1380 |
+
t0 = clock()
|
| 1381 |
+
code = self.shell.compile(expr_ast, source, mode)
|
| 1382 |
+
tc = clock() - t0
|
| 1383 |
+
|
| 1384 |
+
# skew measurement as little as possible
|
| 1385 |
+
glob = self.shell.user_ns
|
| 1386 |
+
wtime = time.time
|
| 1387 |
+
# time execution
|
| 1388 |
+
wall_st = wtime()
|
| 1389 |
+
# Track whether to propagate exceptions or exit
|
| 1390 |
+
exit_on_interrupt = False
|
| 1391 |
+
interrupt_occured = False
|
| 1392 |
+
captured_exception = None
|
| 1393 |
+
|
| 1394 |
+
if mode == "eval":
|
| 1395 |
+
st = clock2()
|
| 1396 |
+
try:
|
| 1397 |
+
out = eval(code, glob, local_ns)
|
| 1398 |
+
except KeyboardInterrupt as e:
|
| 1399 |
+
captured_exception = e
|
| 1400 |
+
interrupt_occured = True
|
| 1401 |
+
exit_on_interrupt = True
|
| 1402 |
+
except Exception as e:
|
| 1403 |
+
captured_exception = e
|
| 1404 |
+
interrupt_occured = True
|
| 1405 |
+
if not args.no_raise_error:
|
| 1406 |
+
exit_on_interrupt = True
|
| 1407 |
+
end = clock2()
|
| 1408 |
+
else:
|
| 1409 |
+
st = clock2()
|
| 1410 |
+
try:
|
| 1411 |
+
exec(code, glob, local_ns)
|
| 1412 |
+
out = None
|
| 1413 |
+
# multi-line %%time case
|
| 1414 |
+
if expr_val is not None:
|
| 1415 |
+
code_2 = self.shell.compile(expr_val, source, 'eval')
|
| 1416 |
+
out = eval(code_2, glob, local_ns)
|
| 1417 |
+
except KeyboardInterrupt as e:
|
| 1418 |
+
captured_exception = e
|
| 1419 |
+
interrupt_occured = True
|
| 1420 |
+
exit_on_interrupt = True
|
| 1421 |
+
except Exception as e:
|
| 1422 |
+
captured_exception = e
|
| 1423 |
+
interrupt_occured = True
|
| 1424 |
+
if not args.no_raise_error:
|
| 1425 |
+
exit_on_interrupt = True
|
| 1426 |
+
end = clock2()
|
| 1427 |
+
wall_end = wtime()
|
| 1428 |
+
# Compute actual times and report
|
| 1429 |
+
wall_time = wall_end - wall_st
|
| 1430 |
+
cpu_user = end[0] - st[0]
|
| 1431 |
+
cpu_sys = end[1] - st[1]
|
| 1432 |
+
cpu_tot = cpu_user + cpu_sys
|
| 1433 |
+
# On windows cpu_sys is always zero, so only total is displayed
|
| 1434 |
+
if sys.platform != "win32":
|
| 1435 |
+
print(
|
| 1436 |
+
f"CPU times: user {_format_time(cpu_user)}, sys: {_format_time(cpu_sys)}, total: {_format_time(cpu_tot)}"
|
| 1437 |
+
)
|
| 1438 |
+
else:
|
| 1439 |
+
print(f"CPU times: total: {_format_time(cpu_tot)}")
|
| 1440 |
+
print(f"Wall time: {_format_time(wall_time)}")
|
| 1441 |
+
if tc > tc_min:
|
| 1442 |
+
print(f"Compiler : {_format_time(tc)}")
|
| 1443 |
+
if tp > tp_min:
|
| 1444 |
+
print(f"Parser : {_format_time(tp)}")
|
| 1445 |
+
if interrupt_occured:
|
| 1446 |
+
if exit_on_interrupt and captured_exception:
|
| 1447 |
+
raise captured_exception
|
| 1448 |
+
return
|
| 1449 |
+
return out
|
| 1450 |
+
|
| 1451 |
+
@skip_doctest
|
| 1452 |
+
@line_magic
|
| 1453 |
+
def macro(self, parameter_s=''):
|
| 1454 |
+
"""Define a macro for future re-execution. It accepts ranges of history,
|
| 1455 |
+
filenames or string objects.
|
| 1456 |
+
|
| 1457 |
+
Usage::
|
| 1458 |
+
|
| 1459 |
+
%macro [options] name n1-n2 n3-n4 ... n5 .. n6 ...
|
| 1460 |
+
|
| 1461 |
+
Options:
|
| 1462 |
+
|
| 1463 |
+
-r
|
| 1464 |
+
Use 'raw' input. By default, the 'processed' history is used,
|
| 1465 |
+
so that magics are loaded in their transformed version to valid
|
| 1466 |
+
Python. If this option is given, the raw input as typed at the
|
| 1467 |
+
command line is used instead.
|
| 1468 |
+
|
| 1469 |
+
-q
|
| 1470 |
+
Quiet macro definition. By default, a tag line is printed
|
| 1471 |
+
to indicate the macro has been created, and then the contents of
|
| 1472 |
+
the macro are printed. If this option is given, then no printout
|
| 1473 |
+
is produced once the macro is created.
|
| 1474 |
+
|
| 1475 |
+
This will define a global variable called `name` which is a string
|
| 1476 |
+
made of joining the slices and lines you specify (n1,n2,... numbers
|
| 1477 |
+
above) from your input history into a single string. This variable
|
| 1478 |
+
acts like an automatic function which re-executes those lines as if
|
| 1479 |
+
you had typed them. You just type 'name' at the prompt and the code
|
| 1480 |
+
executes.
|
| 1481 |
+
|
| 1482 |
+
The syntax for indicating input ranges is described in %history.
|
| 1483 |
+
|
| 1484 |
+
Note: as a 'hidden' feature, you can also use traditional python slice
|
| 1485 |
+
notation, where N:M means numbers N through M-1.
|
| 1486 |
+
|
| 1487 |
+
For example, if your history contains (print using %hist -n )::
|
| 1488 |
+
|
| 1489 |
+
44: x=1
|
| 1490 |
+
45: y=3
|
| 1491 |
+
46: z=x+y
|
| 1492 |
+
47: print(x)
|
| 1493 |
+
48: a=5
|
| 1494 |
+
49: print('x',x,'y',y)
|
| 1495 |
+
|
| 1496 |
+
you can create a macro with lines 44 through 47 (included) and line 49
|
| 1497 |
+
called my_macro with::
|
| 1498 |
+
|
| 1499 |
+
In [55]: %macro my_macro 44-47 49
|
| 1500 |
+
|
| 1501 |
+
Now, typing `my_macro` (without quotes) will re-execute all this code
|
| 1502 |
+
in one pass.
|
| 1503 |
+
|
| 1504 |
+
You don't need to give the line-numbers in order, and any given line
|
| 1505 |
+
number can appear multiple times. You can assemble macros with any
|
| 1506 |
+
lines from your input history in any order.
|
| 1507 |
+
|
| 1508 |
+
The macro is a simple object which holds its value in an attribute,
|
| 1509 |
+
but IPython's display system checks for macros and executes them as
|
| 1510 |
+
code instead of printing them when you type their name.
|
| 1511 |
+
|
| 1512 |
+
You can view a macro's contents by explicitly printing it with::
|
| 1513 |
+
|
| 1514 |
+
print(macro_name)
|
| 1515 |
+
|
| 1516 |
+
"""
|
| 1517 |
+
opts,args = self.parse_options(parameter_s,'rq',mode='list')
|
| 1518 |
+
if not args: # List existing macros
|
| 1519 |
+
return sorted(k for k,v in self.shell.user_ns.items() if isinstance(v, Macro))
|
| 1520 |
+
if len(args) == 1:
|
| 1521 |
+
raise UsageError(
|
| 1522 |
+
"%macro insufficient args; usage '%macro name n1-n2 n3-4...")
|
| 1523 |
+
name, codefrom = args[0], " ".join(args[1:])
|
| 1524 |
+
|
| 1525 |
+
# print('rng',ranges) # dbg
|
| 1526 |
+
try:
|
| 1527 |
+
lines = self.shell.find_user_code(codefrom, 'r' in opts)
|
| 1528 |
+
except (ValueError, TypeError) as e:
|
| 1529 |
+
print(e.args[0])
|
| 1530 |
+
return
|
| 1531 |
+
macro = Macro(lines)
|
| 1532 |
+
self.shell.define_macro(name, macro)
|
| 1533 |
+
if "q" not in opts:
|
| 1534 |
+
print(
|
| 1535 |
+
"Macro `%s` created. To execute, type its name (without quotes)." % name
|
| 1536 |
+
)
|
| 1537 |
+
print("=== Macro contents: ===")
|
| 1538 |
+
print(macro, end=" ")
|
| 1539 |
+
|
| 1540 |
+
@magic_arguments.magic_arguments()
|
| 1541 |
+
@magic_arguments.argument(
|
| 1542 |
+
"output",
|
| 1543 |
+
type=str,
|
| 1544 |
+
default="",
|
| 1545 |
+
nargs="?",
|
| 1546 |
+
help="""
|
| 1547 |
+
|
| 1548 |
+
The name of the variable in which to store output.
|
| 1549 |
+
This is a ``utils.io.CapturedIO`` object with stdout/err attributes
|
| 1550 |
+
for the text of the captured output.
|
| 1551 |
+
|
| 1552 |
+
CapturedOutput also has a ``show()`` method for displaying the output,
|
| 1553 |
+
and ``__call__`` as well, so you can use that to quickly display the
|
| 1554 |
+
output.
|
| 1555 |
+
|
| 1556 |
+
If unspecified, captured output is discarded.
|
| 1557 |
+
""",
|
| 1558 |
+
)
|
| 1559 |
+
@magic_arguments.argument(
|
| 1560 |
+
"--no-stderr", action="store_true", help="""Don't capture stderr."""
|
| 1561 |
+
)
|
| 1562 |
+
@magic_arguments.argument(
|
| 1563 |
+
"--no-stdout", action="store_true", help="""Don't capture stdout."""
|
| 1564 |
+
)
|
| 1565 |
+
@magic_arguments.argument(
|
| 1566 |
+
"--no-display",
|
| 1567 |
+
action="store_true",
|
| 1568 |
+
help="""Don't capture IPython's rich display."""
|
| 1569 |
+
)
|
| 1570 |
+
@cell_magic
|
| 1571 |
+
def capture(self, line, cell):
|
| 1572 |
+
"""run the cell, capturing stdout, stderr, and IPython's rich display() calls."""
|
| 1573 |
+
args = magic_arguments.parse_argstring(self.capture, line)
|
| 1574 |
+
out = not args.no_stdout
|
| 1575 |
+
err = not args.no_stderr
|
| 1576 |
+
disp = not args.no_display
|
| 1577 |
+
with capture_output(out, err, disp) as io:
|
| 1578 |
+
self.shell.run_cell(cell)
|
| 1579 |
+
if DisplayHook.semicolon_at_end_of_expression(cell):
|
| 1580 |
+
if args.output in self.shell.user_ns:
|
| 1581 |
+
del self.shell.user_ns[args.output]
|
| 1582 |
+
elif args.output:
|
| 1583 |
+
self.shell.user_ns[args.output] = io
|
| 1584 |
+
|
| 1585 |
+
@skip_doctest
|
| 1586 |
+
@magic_arguments.magic_arguments()
|
| 1587 |
+
@magic_arguments.argument("name", type=str, default="default", nargs="?")
|
| 1588 |
+
@magic_arguments.argument(
|
| 1589 |
+
"--remove", action="store_true", help="remove the current transformer"
|
| 1590 |
+
)
|
| 1591 |
+
@magic_arguments.argument(
|
| 1592 |
+
"--list", action="store_true", help="list existing transformers name"
|
| 1593 |
+
)
|
| 1594 |
+
@magic_arguments.argument(
|
| 1595 |
+
"--list-all",
|
| 1596 |
+
action="store_true",
|
| 1597 |
+
help="list existing transformers name and code template",
|
| 1598 |
+
)
|
| 1599 |
+
@line_cell_magic
|
| 1600 |
+
def code_wrap(self, line, cell=None):
|
| 1601 |
+
"""
|
| 1602 |
+
Simple magic to quickly define a code transformer for all IPython's future input.
|
| 1603 |
+
|
| 1604 |
+
``__code__`` and ``__ret__`` are special variable that represent the code to run
|
| 1605 |
+
and the value of the last expression of ``__code__`` respectively.
|
| 1606 |
+
|
| 1607 |
+
Examples
|
| 1608 |
+
--------
|
| 1609 |
+
|
| 1610 |
+
.. ipython::
|
| 1611 |
+
|
| 1612 |
+
In [1]: %%code_wrap before_after
|
| 1613 |
+
...: print('before')
|
| 1614 |
+
...: __code__
|
| 1615 |
+
...: print('after')
|
| 1616 |
+
...: __ret__
|
| 1617 |
+
|
| 1618 |
+
|
| 1619 |
+
In [2]: 1
|
| 1620 |
+
before
|
| 1621 |
+
after
|
| 1622 |
+
Out[2]: 1
|
| 1623 |
+
|
| 1624 |
+
In [3]: %code_wrap --list
|
| 1625 |
+
before_after
|
| 1626 |
+
|
| 1627 |
+
In [4]: %code_wrap --list-all
|
| 1628 |
+
before_after :
|
| 1629 |
+
print('before')
|
| 1630 |
+
__code__
|
| 1631 |
+
print('after')
|
| 1632 |
+
__ret__
|
| 1633 |
+
|
| 1634 |
+
In [5]: %code_wrap --remove before_after
|
| 1635 |
+
|
| 1636 |
+
"""
|
| 1637 |
+
args = magic_arguments.parse_argstring(self.code_wrap, line)
|
| 1638 |
+
|
| 1639 |
+
if args.list:
|
| 1640 |
+
for name in self._transformers.keys():
|
| 1641 |
+
print(name)
|
| 1642 |
+
return
|
| 1643 |
+
if args.list_all:
|
| 1644 |
+
for name, _t in self._transformers.items():
|
| 1645 |
+
print(name, ":")
|
| 1646 |
+
print(indent(ast.unparse(_t.template), " "))
|
| 1647 |
+
print()
|
| 1648 |
+
return
|
| 1649 |
+
|
| 1650 |
+
to_remove = self._transformers.pop(args.name, None)
|
| 1651 |
+
if to_remove in self.shell.ast_transformers:
|
| 1652 |
+
self.shell.ast_transformers.remove(to_remove)
|
| 1653 |
+
if cell is None or args.remove:
|
| 1654 |
+
return
|
| 1655 |
+
|
| 1656 |
+
_trs = ReplaceCodeTransformer(ast.parse(cell))
|
| 1657 |
+
|
| 1658 |
+
self._transformers[args.name] = _trs
|
| 1659 |
+
self.shell.ast_transformers.append(_trs)
|
| 1660 |
+
|
| 1661 |
+
|
| 1662 |
+
def parse_breakpoint(text, current_file):
|
| 1663 |
+
'''Returns (file, line) for file:line and (current_file, line) for line'''
|
| 1664 |
+
colon = text.find(':')
|
| 1665 |
+
if colon == -1:
|
| 1666 |
+
return current_file, int(text)
|
| 1667 |
+
else:
|
| 1668 |
+
return text[:colon], int(text[colon+1:])
|
| 1669 |
+
|
| 1670 |
+
|
| 1671 |
+
def _format_time(timespan, precision=3):
|
| 1672 |
+
"""Formats the timespan in a human readable form"""
|
| 1673 |
+
|
| 1674 |
+
if timespan >= 60.0:
|
| 1675 |
+
# we have more than a minute, format that in a human readable form
|
| 1676 |
+
# Idea from http://snipplr.com/view/5713/
|
| 1677 |
+
parts = [("d", 60 * 60 * 24), ("h", 60 * 60), ("min", 60), ("s", 1)]
|
| 1678 |
+
time = []
|
| 1679 |
+
leftover = timespan
|
| 1680 |
+
for suffix, length in parts:
|
| 1681 |
+
value = int(leftover / length)
|
| 1682 |
+
if value > 0:
|
| 1683 |
+
leftover = leftover % length
|
| 1684 |
+
time.append("%s%s" % (str(value), suffix))
|
| 1685 |
+
if leftover < 1:
|
| 1686 |
+
break
|
| 1687 |
+
return " ".join(time)
|
| 1688 |
+
|
| 1689 |
+
# Unfortunately characters outside of range(128) can cause problems in
|
| 1690 |
+
# certain terminals.
|
| 1691 |
+
# See bug: https://bugs.launchpad.net/ipython/+bug/348466
|
| 1692 |
+
# Try to prevent crashes by being more secure than it needs to
|
| 1693 |
+
# E.g. eclipse is able to print a µ, but has no sys.stdout.encoding set.
|
| 1694 |
+
units = ["s", "ms", "us", "ns"] # the safe value
|
| 1695 |
+
if hasattr(sys.stdout, "encoding") and sys.stdout.encoding:
|
| 1696 |
+
try:
|
| 1697 |
+
"μ".encode(sys.stdout.encoding)
|
| 1698 |
+
units = ["s", "ms", "μs", "ns"]
|
| 1699 |
+
except:
|
| 1700 |
+
pass
|
| 1701 |
+
scaling = [1, 1e3, 1e6, 1e9]
|
| 1702 |
+
|
| 1703 |
+
if timespan > 0.0:
|
| 1704 |
+
order = min(-int(math.floor(math.log10(timespan)) // 3), 3)
|
| 1705 |
+
else:
|
| 1706 |
+
order = 3
|
| 1707 |
+
return "%.*g %s" % (precision, timespan * scaling[order], units[order])
|
lib/python3.12/site-packages/IPython/core/magics/history.py
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
)
|
| 78 |
+
@argument(
|
| 79 |
+
"-y",
|
| 80 |
+
dest="overwrite",
|
| 81 |
+
help="yes, overwrite filename even if exists",
|
| 82 |
+
action="store_true",
|
| 83 |
+
default=None,
|
| 84 |
+
)
|
| 85 |
+
@argument(
|
| 86 |
+
"-g",
|
| 87 |
+
dest="pattern",
|
| 88 |
+
nargs="*",
|
| 89 |
+
default=None,
|
| 90 |
+
help="""
|
| 91 |
+
treat the arg as a glob pattern to search for in (full) history.
|
| 92 |
+
This includes the saved history (almost all commands ever written).
|
| 93 |
+
The pattern may contain '?' to match one unknown character and '*'
|
| 94 |
+
to match any number of unknown characters. Use '%%hist -g' to show
|
| 95 |
+
full saved history (may be very long).
|
| 96 |
+
""")
|
| 97 |
+
@argument(
|
| 98 |
+
'-l', dest='limit', type=int, nargs='?', default=_unspecified,
|
| 99 |
+
help="""
|
| 100 |
+
get the last n lines from all sessions. Specify n as a single
|
| 101 |
+
arg, or the default is the last 10 lines.
|
| 102 |
+
""")
|
| 103 |
+
@argument(
|
| 104 |
+
'-u', dest='unique', action='store_true',
|
| 105 |
+
help="""
|
| 106 |
+
when searching history using `-g`, show only unique history.
|
| 107 |
+
""")
|
| 108 |
+
@argument('range', nargs='*')
|
| 109 |
+
@skip_doctest
|
| 110 |
+
@line_magic
|
| 111 |
+
def history(self, parameter_s = ''):
|
| 112 |
+
"""Print input history (_i<n> variables), with most recent last.
|
| 113 |
+
|
| 114 |
+
By default, input history is printed without line numbers so it can be
|
| 115 |
+
directly pasted into an editor. Use -n to show them.
|
| 116 |
+
|
| 117 |
+
By default, all input history from the current session is displayed.
|
| 118 |
+
Ranges of history can be indicated using the syntax:
|
| 119 |
+
|
| 120 |
+
``4``
|
| 121 |
+
Line 4, current session
|
| 122 |
+
``4-6``
|
| 123 |
+
Lines 4-6, current session
|
| 124 |
+
``4-``
|
| 125 |
+
Lines 4 onward (to end), current session
|
| 126 |
+
``243/1-5``
|
| 127 |
+
Lines 1-5, session 243
|
| 128 |
+
``~2/``
|
| 129 |
+
All lines of session 2 before current
|
| 130 |
+
``~4/4-``
|
| 131 |
+
Lines 4 onward (to end), session 4 before current
|
| 132 |
+
``~2/7``
|
| 133 |
+
Line 7, session 2 before current
|
| 134 |
+
``~8/1-~6/5``
|
| 135 |
+
From the first line of 8 sessions ago, to the fifth line of 6
|
| 136 |
+
sessions ago.
|
| 137 |
+
|
| 138 |
+
Multiple ranges can be entered, separated by spaces
|
| 139 |
+
|
| 140 |
+
The same syntax is used by %macro, %save, %edit, %rerun
|
| 141 |
+
|
| 142 |
+
Examples
|
| 143 |
+
--------
|
| 144 |
+
::
|
| 145 |
+
|
| 146 |
+
In [6]: %history -n 4-6
|
| 147 |
+
4:a = 12
|
| 148 |
+
5:print(a**2)
|
| 149 |
+
6:%history -n 4-6
|
| 150 |
+
|
| 151 |
+
"""
|
| 152 |
+
|
| 153 |
+
args = parse_argstring(self.history, parameter_s)
|
| 154 |
+
|
| 155 |
+
# For brevity
|
| 156 |
+
history_manager = self.shell.history_manager
|
| 157 |
+
|
| 158 |
+
def _format_lineno(session, line):
|
| 159 |
+
"""Helper function to format line numbers properly."""
|
| 160 |
+
if session in (0, history_manager.session_number):
|
| 161 |
+
return str(line)
|
| 162 |
+
return "%s/%s" % (session, line)
|
| 163 |
+
|
| 164 |
+
# Check if output to specific file was requested.
|
| 165 |
+
outfname = args.filename
|
| 166 |
+
if not outfname:
|
| 167 |
+
outfile = sys.stdout # default
|
| 168 |
+
# We don't want to close stdout at the end!
|
| 169 |
+
close_at_end = False
|
| 170 |
+
else:
|
| 171 |
+
outfname = os.path.expanduser(outfname)
|
| 172 |
+
if args.overwrite is True:
|
| 173 |
+
ans = True
|
| 174 |
+
elif os.path.exists(outfname):
|
| 175 |
+
ans = True
|
| 176 |
+
if sys.stdin.isatty():
|
| 177 |
+
ans = io.ask_yes_no("File %r exists. Overwrite?" % outfname)
|
| 178 |
+
else:
|
| 179 |
+
try:
|
| 180 |
+
ans = io.ask_yes_no("File %r exists. Overwrite?" % outfname)
|
| 181 |
+
except StdinNotImplementedError:
|
| 182 |
+
ans = True
|
| 183 |
+
if not ans:
|
| 184 |
+
print("Aborting.")
|
| 185 |
+
return
|
| 186 |
+
print("Overwriting file.")
|
| 187 |
+
outfile = io_open(outfname, 'w', encoding='utf-8')
|
| 188 |
+
close_at_end = True
|
| 189 |
+
|
| 190 |
+
print_nums = args.print_nums
|
| 191 |
+
get_output = args.get_output
|
| 192 |
+
pyprompts = args.pyprompts
|
| 193 |
+
raw = args.raw
|
| 194 |
+
|
| 195 |
+
pattern = None
|
| 196 |
+
limit = None if args.limit is _unspecified else args.limit
|
| 197 |
+
|
| 198 |
+
range_pattern = False
|
| 199 |
+
if args.pattern is not None and not args.range:
|
| 200 |
+
if args.pattern:
|
| 201 |
+
pattern = "*" + " ".join(args.pattern) + "*"
|
| 202 |
+
else:
|
| 203 |
+
pattern = "*"
|
| 204 |
+
hist = history_manager.search(pattern, raw=raw, output=get_output,
|
| 205 |
+
n=limit, unique=args.unique)
|
| 206 |
+
print_nums = True
|
| 207 |
+
elif args.limit is not _unspecified:
|
| 208 |
+
n = 10 if limit is None else limit
|
| 209 |
+
hist = history_manager.get_tail(n, raw=raw, output=get_output)
|
| 210 |
+
else:
|
| 211 |
+
if args.pattern:
|
| 212 |
+
range_pattern = "*" + " ".join(args.pattern) + "*"
|
| 213 |
+
print_nums = True
|
| 214 |
+
hist = history_manager.get_range_by_str(
|
| 215 |
+
" ".join(args.range), raw, get_output
|
| 216 |
+
)
|
| 217 |
+
|
| 218 |
+
# We could be displaying the entire history, so let's not try to pull
|
| 219 |
+
# it into a list in memory. Anything that needs more space will just
|
| 220 |
+
# misalign.
|
| 221 |
+
width = 4
|
| 222 |
+
|
| 223 |
+
for session, lineno, inline in hist:
|
| 224 |
+
# Print user history with tabs expanded to 4 spaces. The GUI
|
| 225 |
+
# clients use hard tabs for easier usability in auto-indented code,
|
| 226 |
+
# but we want to produce PEP-8 compliant history for safe pasting
|
| 227 |
+
# into an editor.
|
| 228 |
+
if get_output:
|
| 229 |
+
inline, output = inline
|
| 230 |
+
if range_pattern:
|
| 231 |
+
if not fnmatch.fnmatch(inline, range_pattern):
|
| 232 |
+
continue
|
| 233 |
+
inline = inline.expandtabs(4).rstrip()
|
| 234 |
+
|
| 235 |
+
multiline = "\n" in inline
|
| 236 |
+
line_sep = '\n' if multiline else ' '
|
| 237 |
+
if print_nums:
|
| 238 |
+
print(u'%s:%s' % (_format_lineno(session, lineno).rjust(width),
|
| 239 |
+
line_sep), file=outfile, end=u'')
|
| 240 |
+
if pyprompts:
|
| 241 |
+
print(u">>> ", end=u"", file=outfile)
|
| 242 |
+
if multiline:
|
| 243 |
+
inline = "\n... ".join(inline.splitlines()) + "\n..."
|
| 244 |
+
print(inline, file=outfile)
|
| 245 |
+
if get_output and output:
|
| 246 |
+
print(output, file=outfile)
|
| 247 |
+
|
| 248 |
+
if close_at_end:
|
| 249 |
+
outfile.close()
|
| 250 |
+
|
| 251 |
+
@line_magic
|
| 252 |
+
def recall(self, arg):
|
| 253 |
+
r"""Repeat a command, or get command to input line for editing.
|
| 254 |
+
|
| 255 |
+
%recall and %rep are equivalent.
|
| 256 |
+
|
| 257 |
+
- %recall (no arguments):
|
| 258 |
+
|
| 259 |
+
Place a string version of last computation result (stored in the
|
| 260 |
+
special '_' variable) to the next input prompt. Allows you to create
|
| 261 |
+
elaborate command lines without using copy-paste::
|
| 262 |
+
|
| 263 |
+
In[1]: l = ["hei", "vaan"]
|
| 264 |
+
In[2]: "".join(l)
|
| 265 |
+
Out[2]: heivaan
|
| 266 |
+
In[3]: %recall
|
| 267 |
+
In[4]: heivaan_ <== cursor blinking
|
| 268 |
+
|
| 269 |
+
%recall 45
|
| 270 |
+
|
| 271 |
+
Place history line 45 on the next input prompt. Use %hist to find
|
| 272 |
+
out the number.
|
| 273 |
+
|
| 274 |
+
%recall 1-4
|
| 275 |
+
|
| 276 |
+
Combine the specified lines into one cell, and place it on the next
|
| 277 |
+
input prompt. See %history for the slice syntax.
|
| 278 |
+
|
| 279 |
+
%recall foo+bar
|
| 280 |
+
|
| 281 |
+
If foo+bar can be evaluated in the user namespace, the result is
|
| 282 |
+
placed at the next input prompt. Otherwise, the history is searched
|
| 283 |
+
for lines which contain that substring, and the most recent one is
|
| 284 |
+
placed at the next input prompt.
|
| 285 |
+
"""
|
| 286 |
+
if not arg: # Last output
|
| 287 |
+
self.shell.set_next_input(str(self.shell.user_ns["_"]))
|
| 288 |
+
return
|
| 289 |
+
# Get history range
|
| 290 |
+
histlines = self.shell.history_manager.get_range_by_str(arg)
|
| 291 |
+
cmd = "\n".join(x[2] for x in histlines)
|
| 292 |
+
if cmd:
|
| 293 |
+
self.shell.set_next_input(cmd.rstrip())
|
| 294 |
+
return
|
| 295 |
+
|
| 296 |
+
try: # Variable in user namespace
|
| 297 |
+
cmd = str(eval(arg, self.shell.user_ns))
|
| 298 |
+
except Exception: # Search for term in history
|
| 299 |
+
histlines = self.shell.history_manager.search("*"+arg+"*")
|
| 300 |
+
for h in reversed([x[2] for x in histlines]):
|
| 301 |
+
if 'recall' in h or 'rep' in h:
|
| 302 |
+
continue
|
| 303 |
+
self.shell.set_next_input(h.rstrip())
|
| 304 |
+
return
|
| 305 |
+
else:
|
| 306 |
+
self.shell.set_next_input(cmd.rstrip())
|
| 307 |
+
return
|
| 308 |
+
print("Couldn't evaluate or find in history:", arg)
|
| 309 |
+
|
| 310 |
+
@line_magic
|
| 311 |
+
def rerun(self, parameter_s=''):
|
| 312 |
+
"""Re-run previous input
|
| 313 |
+
|
| 314 |
+
By default, you can specify ranges of input history to be repeated
|
| 315 |
+
(as with %history). With no arguments, it will repeat the last line.
|
| 316 |
+
|
| 317 |
+
Options:
|
| 318 |
+
|
| 319 |
+
-l <n> : Repeat the last n lines of input, not including the
|
| 320 |
+
current command.
|
| 321 |
+
|
| 322 |
+
-g foo : Repeat the most recent line which contains foo
|
| 323 |
+
"""
|
| 324 |
+
opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
|
| 325 |
+
if "l" in opts: # Last n lines
|
| 326 |
+
try:
|
| 327 |
+
n = int(opts["l"])
|
| 328 |
+
except ValueError:
|
| 329 |
+
print("Number of lines must be an integer")
|
| 330 |
+
return
|
| 331 |
+
|
| 332 |
+
if n == 0:
|
| 333 |
+
print("Requested 0 last lines - nothing to run")
|
| 334 |
+
return
|
| 335 |
+
elif n < 0:
|
| 336 |
+
print("Number of lines to rerun cannot be negative")
|
| 337 |
+
return
|
| 338 |
+
|
| 339 |
+
hist = self.shell.history_manager.get_tail(n)
|
| 340 |
+
elif "g" in opts: # Search
|
| 341 |
+
p = "*"+opts['g']+"*"
|
| 342 |
+
hist = list(self.shell.history_manager.search(p))
|
| 343 |
+
for l in reversed(hist):
|
| 344 |
+
if "rerun" not in l[2]:
|
| 345 |
+
hist = [l] # The last match which isn't a %rerun
|
| 346 |
+
break
|
| 347 |
+
else:
|
| 348 |
+
hist = [] # No matches except %rerun
|
| 349 |
+
elif args: # Specify history ranges
|
| 350 |
+
hist = self.shell.history_manager.get_range_by_str(args)
|
| 351 |
+
else: # Last line
|
| 352 |
+
hist = self.shell.history_manager.get_tail(1)
|
| 353 |
+
hist = [x[2] for x in hist]
|
| 354 |
+
if not hist:
|
| 355 |
+
print("No lines in history match specification")
|
| 356 |
+
return
|
| 357 |
+
histlines = "\n".join(hist)
|
| 358 |
+
print("=== Executing: ===")
|
| 359 |
+
print(histlines)
|
| 360 |
+
print("=== Output: ===")
|
| 361 |
+
self.shell.run_cell("\n".join(hist), store_history=False)
|
lib/python3.12/site-packages/IPython/core/magics/namespace.py
ADDED
|
@@ -0,0 +1,723 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
- For DataFrame and Series types: their shape.
|
| 365 |
+
|
| 366 |
+
- Everything else: a string representation, snipping their middle if
|
| 367 |
+
too long.
|
| 368 |
+
|
| 369 |
+
Examples
|
| 370 |
+
--------
|
| 371 |
+
Define two variables and list them with whos::
|
| 372 |
+
|
| 373 |
+
In [1]: alpha = 123
|
| 374 |
+
|
| 375 |
+
In [2]: beta = 'test'
|
| 376 |
+
|
| 377 |
+
In [3]: df = pd.DataFrame({"a": range(10), "b": range(10,20)})
|
| 378 |
+
|
| 379 |
+
In [4]: s = df["a"]
|
| 380 |
+
|
| 381 |
+
In [5]: %whos
|
| 382 |
+
Variable Type Data/Info
|
| 383 |
+
--------------------------------
|
| 384 |
+
alpha int 123
|
| 385 |
+
beta str test
|
| 386 |
+
df DataFrame Shape: (10, 2)
|
| 387 |
+
s Series Shape: (10, )
|
| 388 |
+
"""
|
| 389 |
+
|
| 390 |
+
varnames = self.who_ls(parameter_s)
|
| 391 |
+
if not varnames:
|
| 392 |
+
if parameter_s:
|
| 393 |
+
print('No variables match your requested type.')
|
| 394 |
+
else:
|
| 395 |
+
print('Interactive namespace is empty.')
|
| 396 |
+
return
|
| 397 |
+
|
| 398 |
+
# if we have variables, move on...
|
| 399 |
+
|
| 400 |
+
# for these types, show len() instead of data:
|
| 401 |
+
seq_types = ['dict', 'list', 'tuple']
|
| 402 |
+
|
| 403 |
+
# for numpy arrays, display summary info
|
| 404 |
+
ndarray_type = None
|
| 405 |
+
if 'numpy' in sys.modules:
|
| 406 |
+
try:
|
| 407 |
+
from numpy import ndarray
|
| 408 |
+
except ImportError:
|
| 409 |
+
pass
|
| 410 |
+
else:
|
| 411 |
+
ndarray_type = ndarray.__name__
|
| 412 |
+
|
| 413 |
+
# Find all variable names and types so we can figure out column sizes
|
| 414 |
+
|
| 415 |
+
# some types are well known and can be shorter
|
| 416 |
+
abbrevs = {'IPython.core.macro.Macro' : 'Macro'}
|
| 417 |
+
def type_name(v):
|
| 418 |
+
tn = type(v).__name__
|
| 419 |
+
return abbrevs.get(tn,tn)
|
| 420 |
+
|
| 421 |
+
varlist = [self.shell.user_ns[n] for n in varnames]
|
| 422 |
+
|
| 423 |
+
typelist = []
|
| 424 |
+
for vv in varlist:
|
| 425 |
+
tt = type_name(vv)
|
| 426 |
+
|
| 427 |
+
if tt=='instance':
|
| 428 |
+
typelist.append( abbrevs.get(str(vv.__class__),
|
| 429 |
+
str(vv.__class__)))
|
| 430 |
+
else:
|
| 431 |
+
typelist.append(tt)
|
| 432 |
+
|
| 433 |
+
# column labels and # of spaces as separator
|
| 434 |
+
varlabel = 'Variable'
|
| 435 |
+
typelabel = 'Type'
|
| 436 |
+
datalabel = 'Data/Info'
|
| 437 |
+
colsep = 3
|
| 438 |
+
# variable format strings
|
| 439 |
+
vformat = "{0:<{varwidth}}{1:<{typewidth}}"
|
| 440 |
+
aformat = "%s: %s elems, type `%s`, %s bytes"
|
| 441 |
+
# find the size of the columns to format the output nicely
|
| 442 |
+
varwidth = max(max(map(len,varnames)), len(varlabel)) + colsep
|
| 443 |
+
typewidth = max(max(map(len,typelist)), len(typelabel)) + colsep
|
| 444 |
+
# table header
|
| 445 |
+
print(varlabel.ljust(varwidth) + typelabel.ljust(typewidth) + \
|
| 446 |
+
' '+datalabel+'\n' + '-'*(varwidth+typewidth+len(datalabel)+1))
|
| 447 |
+
# and the table itself
|
| 448 |
+
kb = 1024
|
| 449 |
+
Mb = 1048576 # kb**2
|
| 450 |
+
for vname,var,vtype in zip(varnames,varlist,typelist):
|
| 451 |
+
print(vformat.format(vname, vtype, varwidth=varwidth, typewidth=typewidth), end=' ')
|
| 452 |
+
if vtype in seq_types:
|
| 453 |
+
print("n="+str(len(var)))
|
| 454 |
+
elif vtype == ndarray_type:
|
| 455 |
+
vshape = str(var.shape).replace(',','').replace(' ','x')[1:-1]
|
| 456 |
+
if vtype==ndarray_type:
|
| 457 |
+
# numpy
|
| 458 |
+
vsize = var.size
|
| 459 |
+
vbytes = vsize*var.itemsize
|
| 460 |
+
vdtype = var.dtype
|
| 461 |
+
|
| 462 |
+
if vbytes < 100000:
|
| 463 |
+
print(aformat % (vshape, vsize, vdtype, vbytes))
|
| 464 |
+
else:
|
| 465 |
+
print(aformat % (vshape, vsize, vdtype, vbytes), end=' ')
|
| 466 |
+
if vbytes < Mb:
|
| 467 |
+
print("(%s kb)" % (vbytes / kb,))
|
| 468 |
+
else:
|
| 469 |
+
print("(%s Mb)" % (vbytes / Mb,))
|
| 470 |
+
elif vtype in ["DataFrame", "Series"]:
|
| 471 |
+
# Useful for DataFrames and Series
|
| 472 |
+
# Ought to work for both pandas and polars
|
| 473 |
+
print(f"Shape: {var.shape}")
|
| 474 |
+
else:
|
| 475 |
+
try:
|
| 476 |
+
vstr = str(var)
|
| 477 |
+
except UnicodeEncodeError:
|
| 478 |
+
vstr = var.encode(DEFAULT_ENCODING,
|
| 479 |
+
'backslashreplace')
|
| 480 |
+
except:
|
| 481 |
+
vstr = "<object with id %d (str() failed)>" % id(var)
|
| 482 |
+
vstr = vstr.replace('\n', '\\n')
|
| 483 |
+
if len(vstr) < 50:
|
| 484 |
+
print(vstr)
|
| 485 |
+
else:
|
| 486 |
+
print(vstr[:25] + "<...>" + vstr[-25:])
|
| 487 |
+
|
| 488 |
+
@line_magic
|
| 489 |
+
def reset(self, parameter_s=''):
|
| 490 |
+
"""Resets the namespace by removing all names defined by the user, if
|
| 491 |
+
called without arguments, or by removing some types of objects, such
|
| 492 |
+
as everything currently in IPython's In[] and Out[] containers (see
|
| 493 |
+
the parameters for details).
|
| 494 |
+
|
| 495 |
+
Parameters
|
| 496 |
+
----------
|
| 497 |
+
-f
|
| 498 |
+
force reset without asking for confirmation.
|
| 499 |
+
-s
|
| 500 |
+
'Soft' reset: Only clears your namespace, leaving history intact.
|
| 501 |
+
References to objects may be kept. By default (without this option),
|
| 502 |
+
we do a 'hard' reset, giving you a new session and removing all
|
| 503 |
+
references to objects from the current session.
|
| 504 |
+
--aggressive
|
| 505 |
+
Try to aggressively remove modules from sys.modules ; this
|
| 506 |
+
may allow you to reimport Python modules that have been updated and
|
| 507 |
+
pick up changes, but can have unintended consequences.
|
| 508 |
+
|
| 509 |
+
in
|
| 510 |
+
reset input history
|
| 511 |
+
out
|
| 512 |
+
reset output history
|
| 513 |
+
dhist
|
| 514 |
+
reset directory history
|
| 515 |
+
array
|
| 516 |
+
reset only variables that are NumPy arrays
|
| 517 |
+
|
| 518 |
+
See Also
|
| 519 |
+
--------
|
| 520 |
+
reset_selective : invoked as ``%reset_selective``
|
| 521 |
+
|
| 522 |
+
Examples
|
| 523 |
+
--------
|
| 524 |
+
::
|
| 525 |
+
|
| 526 |
+
In [6]: a = 1
|
| 527 |
+
|
| 528 |
+
In [7]: a
|
| 529 |
+
Out[7]: 1
|
| 530 |
+
|
| 531 |
+
In [8]: 'a' in get_ipython().user_ns
|
| 532 |
+
Out[8]: True
|
| 533 |
+
|
| 534 |
+
In [9]: %reset -f
|
| 535 |
+
|
| 536 |
+
In [1]: 'a' in get_ipython().user_ns
|
| 537 |
+
Out[1]: False
|
| 538 |
+
|
| 539 |
+
In [2]: %reset -f in
|
| 540 |
+
Flushing input history
|
| 541 |
+
|
| 542 |
+
In [3]: %reset -f dhist in
|
| 543 |
+
Flushing directory history
|
| 544 |
+
Flushing input history
|
| 545 |
+
|
| 546 |
+
Notes
|
| 547 |
+
-----
|
| 548 |
+
Calling this magic from clients that do not implement standard input,
|
| 549 |
+
such as the ipython notebook interface, will reset the namespace
|
| 550 |
+
without confirmation.
|
| 551 |
+
"""
|
| 552 |
+
opts, args = self.parse_options(parameter_s, "sf", "aggressive", mode="list")
|
| 553 |
+
if "f" in opts:
|
| 554 |
+
ans = True
|
| 555 |
+
else:
|
| 556 |
+
try:
|
| 557 |
+
ans = self.shell.ask_yes_no(
|
| 558 |
+
"Once deleted, variables cannot be recovered. Proceed (y/[n])?",
|
| 559 |
+
default='n')
|
| 560 |
+
except StdinNotImplementedError:
|
| 561 |
+
ans = True
|
| 562 |
+
if not ans:
|
| 563 |
+
print('Nothing done.')
|
| 564 |
+
return
|
| 565 |
+
|
| 566 |
+
if 's' in opts: # Soft reset
|
| 567 |
+
user_ns = self.shell.user_ns
|
| 568 |
+
for i in self.who_ls():
|
| 569 |
+
del(user_ns[i])
|
| 570 |
+
elif len(args) == 0: # Hard reset
|
| 571 |
+
self.shell.reset(new_session=False, aggressive=("aggressive" in opts))
|
| 572 |
+
|
| 573 |
+
# reset in/out/dhist/array: previously extensinions/clearcmd.py
|
| 574 |
+
ip = self.shell
|
| 575 |
+
user_ns = self.shell.user_ns # local lookup, heavily used
|
| 576 |
+
|
| 577 |
+
for target in args:
|
| 578 |
+
target = target.lower() # make matches case insensitive
|
| 579 |
+
if target == 'out':
|
| 580 |
+
print("Flushing output cache (%d entries)" % len(user_ns['_oh']))
|
| 581 |
+
self.shell.displayhook.flush()
|
| 582 |
+
|
| 583 |
+
elif target == 'in':
|
| 584 |
+
print("Flushing input history")
|
| 585 |
+
pc = self.shell.displayhook.prompt_count + 1
|
| 586 |
+
for n in range(1, pc):
|
| 587 |
+
key = '_i'+repr(n)
|
| 588 |
+
user_ns.pop(key,None)
|
| 589 |
+
user_ns.update(dict(_i=u'',_ii=u'',_iii=u''))
|
| 590 |
+
hm = ip.history_manager
|
| 591 |
+
# don't delete these, as %save and %macro depending on the
|
| 592 |
+
# length of these lists to be preserved
|
| 593 |
+
hm.input_hist_parsed[:] = [''] * pc
|
| 594 |
+
hm.input_hist_raw[:] = [''] * pc
|
| 595 |
+
# hm has internal machinery for _i,_ii,_iii, clear it out
|
| 596 |
+
hm._i = hm._ii = hm._iii = hm._i00 = u''
|
| 597 |
+
|
| 598 |
+
elif target == 'array':
|
| 599 |
+
# Support cleaning up numpy arrays
|
| 600 |
+
try:
|
| 601 |
+
from numpy import ndarray
|
| 602 |
+
# This must be done with items and not iteritems because
|
| 603 |
+
# we're going to modify the dict in-place.
|
| 604 |
+
for x,val in list(user_ns.items()):
|
| 605 |
+
if isinstance(val,ndarray):
|
| 606 |
+
del user_ns[x]
|
| 607 |
+
except ImportError:
|
| 608 |
+
print("reset array only works if Numpy is available.")
|
| 609 |
+
|
| 610 |
+
elif target == 'dhist':
|
| 611 |
+
print("Flushing directory history")
|
| 612 |
+
del user_ns['_dh'][:]
|
| 613 |
+
|
| 614 |
+
else:
|
| 615 |
+
print("Don't know how to reset ", end=' ')
|
| 616 |
+
print(target + ", please run `%reset?` for details")
|
| 617 |
+
|
| 618 |
+
gc.collect()
|
| 619 |
+
|
| 620 |
+
@line_magic
|
| 621 |
+
def reset_selective(self, parameter_s=''):
|
| 622 |
+
"""Resets the namespace by removing names defined by the user.
|
| 623 |
+
|
| 624 |
+
Input/Output history are left around in case you need them.
|
| 625 |
+
|
| 626 |
+
%reset_selective [-f] regex
|
| 627 |
+
|
| 628 |
+
No action is taken if regex is not included
|
| 629 |
+
|
| 630 |
+
Options
|
| 631 |
+
-f : force reset without asking for confirmation.
|
| 632 |
+
|
| 633 |
+
See Also
|
| 634 |
+
--------
|
| 635 |
+
reset : invoked as ``%reset``
|
| 636 |
+
|
| 637 |
+
Examples
|
| 638 |
+
--------
|
| 639 |
+
We first fully reset the namespace so your output looks identical to
|
| 640 |
+
this example for pedagogical reasons; in practice you do not need a
|
| 641 |
+
full reset::
|
| 642 |
+
|
| 643 |
+
In [1]: %reset -f
|
| 644 |
+
|
| 645 |
+
Now, with a clean namespace we can make a few variables and use
|
| 646 |
+
``%reset_selective`` to only delete names that match our regexp::
|
| 647 |
+
|
| 648 |
+
In [2]: a=1; b=2; c=3; b1m=4; b2m=5; b3m=6; b4m=7; b2s=8
|
| 649 |
+
|
| 650 |
+
In [3]: who_ls
|
| 651 |
+
Out[3]: ['a', 'b', 'b1m', 'b2m', 'b2s', 'b3m', 'b4m', 'c']
|
| 652 |
+
|
| 653 |
+
In [4]: %reset_selective -f b[2-3]m
|
| 654 |
+
|
| 655 |
+
In [5]: who_ls
|
| 656 |
+
Out[5]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c']
|
| 657 |
+
|
| 658 |
+
In [6]: %reset_selective -f d
|
| 659 |
+
|
| 660 |
+
In [7]: who_ls
|
| 661 |
+
Out[7]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c']
|
| 662 |
+
|
| 663 |
+
In [8]: %reset_selective -f c
|
| 664 |
+
|
| 665 |
+
In [9]: who_ls
|
| 666 |
+
Out[9]: ['a', 'b', 'b1m', 'b2s', 'b4m']
|
| 667 |
+
|
| 668 |
+
In [10]: %reset_selective -f b
|
| 669 |
+
|
| 670 |
+
In [11]: who_ls
|
| 671 |
+
Out[11]: ['a']
|
| 672 |
+
|
| 673 |
+
Notes
|
| 674 |
+
-----
|
| 675 |
+
Calling this magic from clients that do not implement standard input,
|
| 676 |
+
such as the ipython notebook interface, will reset the namespace
|
| 677 |
+
without confirmation.
|
| 678 |
+
"""
|
| 679 |
+
|
| 680 |
+
opts, regex = self.parse_options(parameter_s,'f')
|
| 681 |
+
|
| 682 |
+
if 'f' in opts:
|
| 683 |
+
ans = True
|
| 684 |
+
else:
|
| 685 |
+
try:
|
| 686 |
+
ans = self.shell.ask_yes_no(
|
| 687 |
+
"Once deleted, variables cannot be recovered. Proceed (y/[n])? ",
|
| 688 |
+
default='n')
|
| 689 |
+
except StdinNotImplementedError:
|
| 690 |
+
ans = True
|
| 691 |
+
if not ans:
|
| 692 |
+
print('Nothing done.')
|
| 693 |
+
return
|
| 694 |
+
user_ns = self.shell.user_ns
|
| 695 |
+
if not regex:
|
| 696 |
+
print('No regex pattern specified. Nothing done.')
|
| 697 |
+
return
|
| 698 |
+
else:
|
| 699 |
+
try:
|
| 700 |
+
m = re.compile(regex)
|
| 701 |
+
except TypeError as e:
|
| 702 |
+
raise TypeError('regex must be a string or compiled pattern') from e
|
| 703 |
+
for i in self.who_ls():
|
| 704 |
+
if m.search(i):
|
| 705 |
+
del(user_ns[i])
|
| 706 |
+
|
| 707 |
+
@line_magic
|
| 708 |
+
def xdel(self, parameter_s=''):
|
| 709 |
+
"""Delete a variable, trying to clear it from anywhere that
|
| 710 |
+
IPython's machinery has references to it. By default, this uses
|
| 711 |
+
the identity of the named object in the user namespace to remove
|
| 712 |
+
references held under other names. The object is also removed
|
| 713 |
+
from the output history.
|
| 714 |
+
|
| 715 |
+
Options
|
| 716 |
+
-n : Delete the specified name from all namespaces, without
|
| 717 |
+
checking their identity.
|
| 718 |
+
"""
|
| 719 |
+
opts, varname = self.parse_options(parameter_s,'n')
|
| 720 |
+
try:
|
| 721 |
+
self.shell.del_var(varname, ('n' in opts))
|
| 722 |
+
except (NameError, ValueError) as e:
|
| 723 |
+
print(type(e).__name__ +": "+ str(e))
|
lib/python3.12/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)
|
lib/python3.12/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.")
|
lib/python3.12/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)
|
lib/python3.12/site-packages/IPython/external/__init__.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This package contains all third-party modules bundled with IPython.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from typing import List
|
| 6 |
+
|
| 7 |
+
__all__: List[str] = []
|
lib/python3.12/site-packages/IPython/external/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (396 Bytes). View file
|
|
|
lib/python3.12/site-packages/IPython/external/__pycache__/pickleshare.cpython-312.pyc
ADDED
|
Binary file (13.2 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/external/__pycache__/qt_for_kernel.cpython-312.pyc
ADDED
|
Binary file (3.69 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/external/__pycache__/qt_loaders.cpython-312.pyc
ADDED
|
Binary file (14.2 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/external/pickleshare.py
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Vendoring of pickleshare, reduced to used functionalities.
|
| 3 |
+
|
| 4 |
+
---
|
| 5 |
+
|
| 6 |
+
PickleShare - a small 'shelve' like datastore with concurrency support
|
| 7 |
+
|
| 8 |
+
Like shelve, a PickleShareDB object acts like a normal dictionary. Unlike
|
| 9 |
+
shelve, many processes can access the database simultaneously. Changing a
|
| 10 |
+
value in database is immediately visible to other processes accessing the
|
| 11 |
+
same database.
|
| 12 |
+
|
| 13 |
+
Concurrency is possible because the values are stored in separate files. Hence
|
| 14 |
+
the "database" is a directory where *all* files are governed by PickleShare.
|
| 15 |
+
|
| 16 |
+
Example usage::
|
| 17 |
+
|
| 18 |
+
from pickleshare import *
|
| 19 |
+
db = PickleShareDB('~/testpickleshare')
|
| 20 |
+
db.clear()
|
| 21 |
+
print "Should be empty:",db.items()
|
| 22 |
+
db['hello'] = 15
|
| 23 |
+
db['aku ankka'] = [1,2,313]
|
| 24 |
+
db['paths/are/ok/key'] = [1,(5,46)]
|
| 25 |
+
print db.keys()
|
| 26 |
+
del db['aku ankka']
|
| 27 |
+
|
| 28 |
+
This module is certainly not ZODB, but can be used for low-load
|
| 29 |
+
(non-mission-critical) situations where tiny code size trumps the
|
| 30 |
+
advanced features of a "real" object database.
|
| 31 |
+
|
| 32 |
+
Installation guide: pip install pickleshare
|
| 33 |
+
|
| 34 |
+
Author: Ville Vainio <vivainio@gmail.com>
|
| 35 |
+
License: MIT open source license.
|
| 36 |
+
|
| 37 |
+
"""
|
| 38 |
+
|
| 39 |
+
__version__ = "0.7.5"
|
| 40 |
+
|
| 41 |
+
from pathlib import Path
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
import os, stat, time
|
| 45 |
+
|
| 46 |
+
try:
|
| 47 |
+
import collections.abc as collections_abc
|
| 48 |
+
except ImportError:
|
| 49 |
+
import collections as collections_abc
|
| 50 |
+
try:
|
| 51 |
+
import cPickle as pickle
|
| 52 |
+
except ImportError:
|
| 53 |
+
import pickle
|
| 54 |
+
import errno
|
| 55 |
+
import sys
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def gethashfile(key):
|
| 59 |
+
return ("%02x" % abs(hash(key) % 256))[-2:]
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
_sentinel = object()
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
class PickleShareDB(collections_abc.MutableMapping):
|
| 66 |
+
"""The main 'connection' object for PickleShare database"""
|
| 67 |
+
|
| 68 |
+
def __init__(self, root):
|
| 69 |
+
"""Return a db object that will manage the specied directory"""
|
| 70 |
+
if not isinstance(root, str):
|
| 71 |
+
root = str(root)
|
| 72 |
+
root = os.path.abspath(os.path.expanduser(root))
|
| 73 |
+
self.root = Path(root)
|
| 74 |
+
if not self.root.is_dir():
|
| 75 |
+
# catching the exception is necessary if multiple processes are concurrently trying to create a folder
|
| 76 |
+
# exists_ok keyword argument of mkdir does the same but only from Python 3.5
|
| 77 |
+
try:
|
| 78 |
+
self.root.mkdir(parents=True)
|
| 79 |
+
except OSError as e:
|
| 80 |
+
if e.errno != errno.EEXIST:
|
| 81 |
+
raise
|
| 82 |
+
# cache has { 'key' : (obj, orig_mod_time) }
|
| 83 |
+
self.cache = {}
|
| 84 |
+
|
| 85 |
+
def __getitem__(self, key):
|
| 86 |
+
"""db['key'] reading"""
|
| 87 |
+
fil = self.root / key
|
| 88 |
+
try:
|
| 89 |
+
mtime = fil.stat()[stat.ST_MTIME]
|
| 90 |
+
except OSError:
|
| 91 |
+
raise KeyError(key)
|
| 92 |
+
|
| 93 |
+
if fil in self.cache and mtime == self.cache[fil][1]:
|
| 94 |
+
return self.cache[fil][0]
|
| 95 |
+
try:
|
| 96 |
+
# The cached item has expired, need to read
|
| 97 |
+
with fil.open("rb") as f:
|
| 98 |
+
obj = pickle.loads(f.read())
|
| 99 |
+
except:
|
| 100 |
+
raise KeyError(key)
|
| 101 |
+
|
| 102 |
+
self.cache[fil] = (obj, mtime)
|
| 103 |
+
return obj
|
| 104 |
+
|
| 105 |
+
def __setitem__(self, key, value):
|
| 106 |
+
"""db['key'] = 5"""
|
| 107 |
+
fil = self.root / key
|
| 108 |
+
parent = fil.parent
|
| 109 |
+
if parent and not parent.is_dir():
|
| 110 |
+
parent.mkdir(parents=True)
|
| 111 |
+
# We specify protocol 2, so that we can mostly go between Python 2
|
| 112 |
+
# and Python 3. We can upgrade to protocol 3 when Python 2 is obsolete.
|
| 113 |
+
with fil.open("wb") as f:
|
| 114 |
+
pickle.dump(value, f, protocol=2)
|
| 115 |
+
try:
|
| 116 |
+
self.cache[fil] = (value, fil.stat().st_mtime)
|
| 117 |
+
except OSError as e:
|
| 118 |
+
if e.errno != errno.ENOENT:
|
| 119 |
+
raise
|
| 120 |
+
|
| 121 |
+
def hset(self, hashroot, key, value):
|
| 122 |
+
"""hashed set"""
|
| 123 |
+
hroot = self.root / hashroot
|
| 124 |
+
if not hroot.is_dir():
|
| 125 |
+
hroot.mkdir()
|
| 126 |
+
hfile = hroot / gethashfile(key)
|
| 127 |
+
d = self.get(hfile, {})
|
| 128 |
+
d.update({key: value})
|
| 129 |
+
self[hfile] = d
|
| 130 |
+
|
| 131 |
+
def hget(self, hashroot, key, default=_sentinel, fast_only=True):
|
| 132 |
+
"""hashed get"""
|
| 133 |
+
hroot = self.root / hashroot
|
| 134 |
+
hfile = hroot / gethashfile(key)
|
| 135 |
+
|
| 136 |
+
d = self.get(hfile, _sentinel)
|
| 137 |
+
# print "got dict",d,"from",hfile
|
| 138 |
+
if d is _sentinel:
|
| 139 |
+
if fast_only:
|
| 140 |
+
if default is _sentinel:
|
| 141 |
+
raise KeyError(key)
|
| 142 |
+
|
| 143 |
+
return default
|
| 144 |
+
|
| 145 |
+
# slow mode ok, works even after hcompress()
|
| 146 |
+
d = self.hdict(hashroot)
|
| 147 |
+
|
| 148 |
+
return d.get(key, default)
|
| 149 |
+
|
| 150 |
+
def hdict(self, hashroot):
|
| 151 |
+
"""Get all data contained in hashed category 'hashroot' as dict"""
|
| 152 |
+
hfiles = self.keys(hashroot + "/*")
|
| 153 |
+
hfiles.sort()
|
| 154 |
+
last = len(hfiles) and hfiles[-1] or ""
|
| 155 |
+
if last.endswith("xx"):
|
| 156 |
+
# print "using xx"
|
| 157 |
+
hfiles = [last] + hfiles[:-1]
|
| 158 |
+
|
| 159 |
+
all = {}
|
| 160 |
+
|
| 161 |
+
for f in hfiles:
|
| 162 |
+
# print "using",f
|
| 163 |
+
try:
|
| 164 |
+
all.update(self[f])
|
| 165 |
+
except KeyError:
|
| 166 |
+
print("Corrupt", f, "deleted - hset is not threadsafe!")
|
| 167 |
+
del self[f]
|
| 168 |
+
|
| 169 |
+
self.uncache(f)
|
| 170 |
+
|
| 171 |
+
return all
|
| 172 |
+
|
| 173 |
+
def hcompress(self, hashroot):
|
| 174 |
+
"""Compress category 'hashroot', so hset is fast again
|
| 175 |
+
|
| 176 |
+
hget will fail if fast_only is True for compressed items (that were
|
| 177 |
+
hset before hcompress).
|
| 178 |
+
|
| 179 |
+
"""
|
| 180 |
+
hfiles = self.keys(hashroot + "/*")
|
| 181 |
+
all = {}
|
| 182 |
+
for f in hfiles:
|
| 183 |
+
# print "using",f
|
| 184 |
+
all.update(self[f])
|
| 185 |
+
self.uncache(f)
|
| 186 |
+
|
| 187 |
+
self[hashroot + "/xx"] = all
|
| 188 |
+
for f in hfiles:
|
| 189 |
+
p = self.root / f
|
| 190 |
+
if p.name == "xx":
|
| 191 |
+
continue
|
| 192 |
+
p.unlink()
|
| 193 |
+
|
| 194 |
+
def __delitem__(self, key):
|
| 195 |
+
"""del db["key"]"""
|
| 196 |
+
fil = self.root / key
|
| 197 |
+
self.cache.pop(fil, None)
|
| 198 |
+
try:
|
| 199 |
+
fil.unlink()
|
| 200 |
+
except OSError:
|
| 201 |
+
# notfound and permission denied are ok - we
|
| 202 |
+
# lost, the other process wins the conflict
|
| 203 |
+
pass
|
| 204 |
+
|
| 205 |
+
def _normalized(self, p):
|
| 206 |
+
"""Make a key suitable for user's eyes"""
|
| 207 |
+
return str(p.relative_to(self.root)).replace("\\", "/")
|
| 208 |
+
|
| 209 |
+
def keys(self, globpat=None):
|
| 210 |
+
"""All keys in DB, or all keys matching a glob"""
|
| 211 |
+
|
| 212 |
+
if globpat is None:
|
| 213 |
+
files = self.root.rglob("*")
|
| 214 |
+
else:
|
| 215 |
+
files = self.root.glob(globpat)
|
| 216 |
+
return [self._normalized(p) for p in files if p.is_file()]
|
| 217 |
+
|
| 218 |
+
def __iter__(self):
|
| 219 |
+
return iter(self.keys())
|
| 220 |
+
|
| 221 |
+
def __len__(self):
|
| 222 |
+
return len(self.keys())
|
| 223 |
+
|
| 224 |
+
def uncache(self, *items):
|
| 225 |
+
"""Removes all, or specified items from cache
|
| 226 |
+
|
| 227 |
+
Use this after reading a large amount of large objects
|
| 228 |
+
to free up memory, when you won't be needing the objects
|
| 229 |
+
for a while.
|
| 230 |
+
|
| 231 |
+
"""
|
| 232 |
+
if not items:
|
| 233 |
+
self.cache = {}
|
| 234 |
+
for it in items:
|
| 235 |
+
self.cache.pop(it, None)
|
| 236 |
+
|
| 237 |
+
def waitget(self, key, maxwaittime=60):
|
| 238 |
+
"""Wait (poll) for a key to get a value
|
| 239 |
+
|
| 240 |
+
Will wait for `maxwaittime` seconds before raising a KeyError.
|
| 241 |
+
The call exits normally if the `key` field in db gets a value
|
| 242 |
+
within the timeout period.
|
| 243 |
+
|
| 244 |
+
Use this for synchronizing different processes or for ensuring
|
| 245 |
+
that an unfortunately timed "db['key'] = newvalue" operation
|
| 246 |
+
in another process (which causes all 'get' operation to cause a
|
| 247 |
+
KeyError for the duration of pickling) won't screw up your program
|
| 248 |
+
logic.
|
| 249 |
+
"""
|
| 250 |
+
|
| 251 |
+
wtimes = [0.2] * 3 + [0.5] * 2 + [1]
|
| 252 |
+
tries = 0
|
| 253 |
+
waited = 0
|
| 254 |
+
while 1:
|
| 255 |
+
try:
|
| 256 |
+
val = self[key]
|
| 257 |
+
return val
|
| 258 |
+
except KeyError:
|
| 259 |
+
pass
|
| 260 |
+
|
| 261 |
+
if waited > maxwaittime:
|
| 262 |
+
raise KeyError(key)
|
| 263 |
+
|
| 264 |
+
time.sleep(wtimes[tries])
|
| 265 |
+
waited += wtimes[tries]
|
| 266 |
+
if tries < len(wtimes) - 1:
|
| 267 |
+
tries += 1
|
| 268 |
+
|
| 269 |
+
def getlink(self, folder):
|
| 270 |
+
"""Get a convenient link for accessing items"""
|
| 271 |
+
return PickleShareLink(self, folder)
|
| 272 |
+
|
| 273 |
+
def __repr__(self):
|
| 274 |
+
return "PickleShareDB('%s')" % self.root
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
class PickleShareLink:
|
| 278 |
+
"""A shortdand for accessing nested PickleShare data conveniently.
|
| 279 |
+
|
| 280 |
+
Created through PickleShareDB.getlink(), example::
|
| 281 |
+
|
| 282 |
+
lnk = db.getlink('myobjects/test')
|
| 283 |
+
lnk.foo = 2
|
| 284 |
+
lnk.bar = lnk.foo + 5
|
| 285 |
+
|
| 286 |
+
"""
|
| 287 |
+
|
| 288 |
+
def __init__(self, db, keydir):
|
| 289 |
+
self.__dict__.update(locals())
|
| 290 |
+
|
| 291 |
+
def __getattr__(self, key):
|
| 292 |
+
return self.__dict__["db"][self.__dict__["keydir"] + "/" + key]
|
| 293 |
+
|
| 294 |
+
def __setattr__(self, key, val):
|
| 295 |
+
self.db[self.keydir + "/" + key] = val
|
| 296 |
+
|
| 297 |
+
def __repr__(self):
|
| 298 |
+
db = self.__dict__["db"]
|
| 299 |
+
keys = db.keys(self.__dict__["keydir"] + "/*")
|
| 300 |
+
return "<PickleShareLink '%s': %s>" % (
|
| 301 |
+
self.__dict__["keydir"],
|
| 302 |
+
";".join([Path(k).basename() for k in keys]),
|
| 303 |
+
)
|
lib/python3.12/site-packages/IPython/external/qt_for_kernel.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Import Qt in a manner suitable for an IPython kernel.
|
| 2 |
+
|
| 3 |
+
This is the import used for the `gui=qt` or `matplotlib=qt` initialization.
|
| 4 |
+
|
| 5 |
+
Import Priority:
|
| 6 |
+
|
| 7 |
+
if Qt has been imported anywhere else:
|
| 8 |
+
use that
|
| 9 |
+
|
| 10 |
+
if matplotlib has been imported and doesn't support v2 (<= 1.0.1):
|
| 11 |
+
use PyQt4 @v1
|
| 12 |
+
|
| 13 |
+
Next, ask QT_API env variable
|
| 14 |
+
|
| 15 |
+
if QT_API not set:
|
| 16 |
+
ask matplotlib what it's using. If Qt4Agg or Qt5Agg, then use the
|
| 17 |
+
version matplotlib is configured with
|
| 18 |
+
|
| 19 |
+
else: (matplotlib said nothing)
|
| 20 |
+
# this is the default path - nobody told us anything
|
| 21 |
+
try in this order:
|
| 22 |
+
PyQt default version, PySide, PyQt5
|
| 23 |
+
else:
|
| 24 |
+
use what QT_API says
|
| 25 |
+
|
| 26 |
+
Note that %gui's implementation will always set a `QT_API`, see
|
| 27 |
+
`IPython.terminal.pt_inputhooks.get_inputhook_name_and_func`
|
| 28 |
+
|
| 29 |
+
"""
|
| 30 |
+
# NOTE: This is no longer an external, third-party module, and should be
|
| 31 |
+
# considered part of IPython. For compatibility however, it is being kept in
|
| 32 |
+
# IPython/external.
|
| 33 |
+
|
| 34 |
+
import os
|
| 35 |
+
import sys
|
| 36 |
+
|
| 37 |
+
from IPython.external.qt_loaders import (
|
| 38 |
+
load_qt,
|
| 39 |
+
loaded_api,
|
| 40 |
+
enum_factory,
|
| 41 |
+
# QT6
|
| 42 |
+
QT_API_PYQT6,
|
| 43 |
+
QT_API_PYSIDE6,
|
| 44 |
+
# QT5
|
| 45 |
+
QT_API_PYQT5,
|
| 46 |
+
QT_API_PYSIDE2,
|
| 47 |
+
# QT4
|
| 48 |
+
QT_API_PYQT,
|
| 49 |
+
QT_API_PYSIDE,
|
| 50 |
+
# default
|
| 51 |
+
QT_API_PYQT_DEFAULT,
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
_qt_apis = (
|
| 55 |
+
# QT6
|
| 56 |
+
QT_API_PYQT6,
|
| 57 |
+
QT_API_PYSIDE6,
|
| 58 |
+
# QT5
|
| 59 |
+
QT_API_PYQT5,
|
| 60 |
+
QT_API_PYSIDE2,
|
| 61 |
+
# default
|
| 62 |
+
QT_API_PYQT_DEFAULT,
|
| 63 |
+
)
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def matplotlib_options(mpl):
|
| 67 |
+
"""Constraints placed on an imported matplotlib."""
|
| 68 |
+
if mpl is None:
|
| 69 |
+
return
|
| 70 |
+
backend = mpl.rcParams.get('backend', None)
|
| 71 |
+
if backend == 'Qt4Agg':
|
| 72 |
+
mpqt = mpl.rcParams.get('backend.qt4', None)
|
| 73 |
+
if mpqt is None:
|
| 74 |
+
return None
|
| 75 |
+
if mpqt.lower() == 'pyside':
|
| 76 |
+
return [QT_API_PYSIDE]
|
| 77 |
+
elif mpqt.lower() == 'pyqt4':
|
| 78 |
+
return [QT_API_PYQT_DEFAULT]
|
| 79 |
+
elif mpqt.lower() == 'pyqt4v2':
|
| 80 |
+
return [QT_API_PYQT]
|
| 81 |
+
raise ImportError("unhandled value for backend.qt4 from matplotlib: %r" %
|
| 82 |
+
mpqt)
|
| 83 |
+
elif backend == 'Qt5Agg':
|
| 84 |
+
mpqt = mpl.rcParams.get('backend.qt5', None)
|
| 85 |
+
if mpqt is None:
|
| 86 |
+
return None
|
| 87 |
+
if mpqt.lower() == 'pyqt5':
|
| 88 |
+
return [QT_API_PYQT5]
|
| 89 |
+
raise ImportError("unhandled value for backend.qt5 from matplotlib: %r" %
|
| 90 |
+
mpqt)
|
| 91 |
+
|
| 92 |
+
def get_options():
|
| 93 |
+
"""Return a list of acceptable QT APIs, in decreasing order of preference."""
|
| 94 |
+
#already imported Qt somewhere. Use that
|
| 95 |
+
loaded = loaded_api()
|
| 96 |
+
if loaded is not None:
|
| 97 |
+
return [loaded]
|
| 98 |
+
|
| 99 |
+
mpl = sys.modules.get("matplotlib", None)
|
| 100 |
+
|
| 101 |
+
if mpl is not None and tuple(mpl.__version__.split(".")) < ("1", "0", "2"):
|
| 102 |
+
# 1.0.1 only supports PyQt4 v1
|
| 103 |
+
return [QT_API_PYQT_DEFAULT]
|
| 104 |
+
|
| 105 |
+
qt_api = os.environ.get('QT_API', None)
|
| 106 |
+
if qt_api is None:
|
| 107 |
+
#no ETS variable. Ask mpl, then use default fallback path
|
| 108 |
+
return matplotlib_options(mpl) or [
|
| 109 |
+
QT_API_PYQT_DEFAULT,
|
| 110 |
+
QT_API_PYQT6,
|
| 111 |
+
QT_API_PYSIDE6,
|
| 112 |
+
QT_API_PYQT5,
|
| 113 |
+
QT_API_PYSIDE2,
|
| 114 |
+
]
|
| 115 |
+
elif qt_api not in _qt_apis:
|
| 116 |
+
raise RuntimeError("Invalid Qt API %r, valid values are: %r" %
|
| 117 |
+
(qt_api, ', '.join(_qt_apis)))
|
| 118 |
+
else:
|
| 119 |
+
return [qt_api]
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
api_opts = get_options()
|
| 123 |
+
QtCore, QtGui, QtSvg, QT_API = load_qt(api_opts)
|
| 124 |
+
enum_helper = enum_factory(QT_API, QtCore)
|
lib/python3.12/site-packages/IPython/external/qt_loaders.py
ADDED
|
@@ -0,0 +1,423 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This module contains factory functions that attempt
|
| 3 |
+
to return Qt submodules from the various python Qt bindings.
|
| 4 |
+
|
| 5 |
+
It also protects against double-importing Qt with different
|
| 6 |
+
bindings, which is unstable and likely to crash
|
| 7 |
+
|
| 8 |
+
This is used primarily by qt and qt_for_kernel, and shouldn't
|
| 9 |
+
be accessed directly from the outside
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
import importlib.abc
|
| 13 |
+
import sys
|
| 14 |
+
import os
|
| 15 |
+
import types
|
| 16 |
+
from functools import partial, lru_cache
|
| 17 |
+
import operator
|
| 18 |
+
|
| 19 |
+
# ### Available APIs.
|
| 20 |
+
# Qt6
|
| 21 |
+
QT_API_PYQT6 = "pyqt6"
|
| 22 |
+
QT_API_PYSIDE6 = "pyside6"
|
| 23 |
+
|
| 24 |
+
# Qt5
|
| 25 |
+
QT_API_PYQT5 = 'pyqt5'
|
| 26 |
+
QT_API_PYSIDE2 = 'pyside2'
|
| 27 |
+
|
| 28 |
+
# Qt4
|
| 29 |
+
# NOTE: Here for legacy matplotlib compatibility, but not really supported on the IPython side.
|
| 30 |
+
QT_API_PYQT = "pyqt" # Force version 2
|
| 31 |
+
QT_API_PYQTv1 = "pyqtv1" # Force version 2
|
| 32 |
+
QT_API_PYSIDE = "pyside"
|
| 33 |
+
|
| 34 |
+
QT_API_PYQT_DEFAULT = "pyqtdefault" # use system default for version 1 vs. 2
|
| 35 |
+
|
| 36 |
+
api_to_module = {
|
| 37 |
+
# Qt6
|
| 38 |
+
QT_API_PYQT6: "PyQt6",
|
| 39 |
+
QT_API_PYSIDE6: "PySide6",
|
| 40 |
+
# Qt5
|
| 41 |
+
QT_API_PYQT5: "PyQt5",
|
| 42 |
+
QT_API_PYSIDE2: "PySide2",
|
| 43 |
+
# Qt4
|
| 44 |
+
QT_API_PYSIDE: "PySide",
|
| 45 |
+
QT_API_PYQT: "PyQt4",
|
| 46 |
+
QT_API_PYQTv1: "PyQt4",
|
| 47 |
+
# default
|
| 48 |
+
QT_API_PYQT_DEFAULT: "PyQt6",
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
class ImportDenier(importlib.abc.MetaPathFinder):
|
| 53 |
+
"""Import Hook that will guard against bad Qt imports
|
| 54 |
+
once IPython commits to a specific binding
|
| 55 |
+
"""
|
| 56 |
+
|
| 57 |
+
def __init__(self):
|
| 58 |
+
self.__forbidden = set()
|
| 59 |
+
|
| 60 |
+
def forbid(self, module_name):
|
| 61 |
+
sys.modules.pop(module_name, None)
|
| 62 |
+
self.__forbidden.add(module_name)
|
| 63 |
+
|
| 64 |
+
def find_spec(self, fullname, path, target=None):
|
| 65 |
+
if path:
|
| 66 |
+
return
|
| 67 |
+
if fullname in self.__forbidden:
|
| 68 |
+
raise ImportError(
|
| 69 |
+
"""
|
| 70 |
+
Importing %s disabled by IPython, which has
|
| 71 |
+
already imported an Incompatible QT Binding: %s
|
| 72 |
+
"""
|
| 73 |
+
% (fullname, loaded_api())
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
ID = ImportDenier()
|
| 78 |
+
sys.meta_path.insert(0, ID)
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def commit_api(api):
|
| 82 |
+
"""Commit to a particular API, and trigger ImportErrors on subsequent
|
| 83 |
+
dangerous imports"""
|
| 84 |
+
modules = set(api_to_module.values())
|
| 85 |
+
|
| 86 |
+
modules.remove(api_to_module[api])
|
| 87 |
+
for mod in modules:
|
| 88 |
+
ID.forbid(mod)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def loaded_api():
|
| 92 |
+
"""Return which API is loaded, if any
|
| 93 |
+
|
| 94 |
+
If this returns anything besides None,
|
| 95 |
+
importing any other Qt binding is unsafe.
|
| 96 |
+
|
| 97 |
+
Returns
|
| 98 |
+
-------
|
| 99 |
+
None, 'pyside6', 'pyqt6', 'pyside2', 'pyside', 'pyqt', 'pyqt5', 'pyqtv1'
|
| 100 |
+
"""
|
| 101 |
+
if sys.modules.get("PyQt6.QtCore"):
|
| 102 |
+
return QT_API_PYQT6
|
| 103 |
+
elif sys.modules.get("PySide6.QtCore"):
|
| 104 |
+
return QT_API_PYSIDE6
|
| 105 |
+
elif sys.modules.get("PyQt5.QtCore"):
|
| 106 |
+
return QT_API_PYQT5
|
| 107 |
+
elif sys.modules.get("PySide2.QtCore"):
|
| 108 |
+
return QT_API_PYSIDE2
|
| 109 |
+
elif sys.modules.get("PyQt4.QtCore"):
|
| 110 |
+
if qtapi_version() == 2:
|
| 111 |
+
return QT_API_PYQT
|
| 112 |
+
else:
|
| 113 |
+
return QT_API_PYQTv1
|
| 114 |
+
elif sys.modules.get("PySide.QtCore"):
|
| 115 |
+
return QT_API_PYSIDE
|
| 116 |
+
|
| 117 |
+
return None
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def has_binding(api):
|
| 121 |
+
"""Safely check for PyQt4/5, PySide or PySide2, without importing submodules
|
| 122 |
+
|
| 123 |
+
Parameters
|
| 124 |
+
----------
|
| 125 |
+
api : str [ 'pyqtv1' | 'pyqt' | 'pyqt5' | 'pyside' | 'pyside2' | 'pyqtdefault']
|
| 126 |
+
Which module to check for
|
| 127 |
+
|
| 128 |
+
Returns
|
| 129 |
+
-------
|
| 130 |
+
True if the relevant module appears to be importable
|
| 131 |
+
"""
|
| 132 |
+
module_name = api_to_module[api]
|
| 133 |
+
from importlib.util import find_spec
|
| 134 |
+
|
| 135 |
+
required = ['QtCore', 'QtGui', 'QtSvg']
|
| 136 |
+
if api in (QT_API_PYQT5, QT_API_PYSIDE2, QT_API_PYQT6, QT_API_PYSIDE6):
|
| 137 |
+
# QT5 requires QtWidgets too
|
| 138 |
+
required.append('QtWidgets')
|
| 139 |
+
|
| 140 |
+
for submod in required:
|
| 141 |
+
try:
|
| 142 |
+
spec = find_spec('%s.%s' % (module_name, submod))
|
| 143 |
+
except ImportError:
|
| 144 |
+
# Package (e.g. PyQt5) not found
|
| 145 |
+
return False
|
| 146 |
+
else:
|
| 147 |
+
if spec is None:
|
| 148 |
+
# Submodule (e.g. PyQt5.QtCore) not found
|
| 149 |
+
return False
|
| 150 |
+
|
| 151 |
+
if api == QT_API_PYSIDE:
|
| 152 |
+
# We can also safely check PySide version
|
| 153 |
+
import PySide
|
| 154 |
+
|
| 155 |
+
return PySide.__version_info__ >= (1, 0, 3)
|
| 156 |
+
|
| 157 |
+
return True
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
def qtapi_version():
|
| 161 |
+
"""Return which QString API has been set, if any
|
| 162 |
+
|
| 163 |
+
Returns
|
| 164 |
+
-------
|
| 165 |
+
The QString API version (1 or 2), or None if not set
|
| 166 |
+
"""
|
| 167 |
+
try:
|
| 168 |
+
import sip
|
| 169 |
+
except ImportError:
|
| 170 |
+
# as of PyQt5 5.11, sip is no longer available as a top-level
|
| 171 |
+
# module and needs to be imported from the PyQt5 namespace
|
| 172 |
+
try:
|
| 173 |
+
from PyQt5 import sip
|
| 174 |
+
except ImportError:
|
| 175 |
+
return
|
| 176 |
+
try:
|
| 177 |
+
return sip.getapi('QString')
|
| 178 |
+
except ValueError:
|
| 179 |
+
return
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
def can_import(api):
|
| 183 |
+
"""Safely query whether an API is importable, without importing it"""
|
| 184 |
+
if not has_binding(api):
|
| 185 |
+
return False
|
| 186 |
+
|
| 187 |
+
current = loaded_api()
|
| 188 |
+
if api == QT_API_PYQT_DEFAULT:
|
| 189 |
+
return current in [QT_API_PYQT6, None]
|
| 190 |
+
else:
|
| 191 |
+
return current in [api, None]
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
def import_pyqt4(version=2):
|
| 195 |
+
"""
|
| 196 |
+
Import PyQt4
|
| 197 |
+
|
| 198 |
+
Parameters
|
| 199 |
+
----------
|
| 200 |
+
version : 1, 2, or None
|
| 201 |
+
Which QString/QVariant API to use. Set to None to use the system
|
| 202 |
+
default
|
| 203 |
+
ImportErrors raised within this function are non-recoverable
|
| 204 |
+
"""
|
| 205 |
+
# The new-style string API (version=2) automatically
|
| 206 |
+
# converts QStrings to Unicode Python strings. Also, automatically unpacks
|
| 207 |
+
# QVariants to their underlying objects.
|
| 208 |
+
import sip
|
| 209 |
+
|
| 210 |
+
if version is not None:
|
| 211 |
+
sip.setapi('QString', version)
|
| 212 |
+
sip.setapi('QVariant', version)
|
| 213 |
+
|
| 214 |
+
from PyQt4 import QtGui, QtCore, QtSvg
|
| 215 |
+
|
| 216 |
+
if QtCore.PYQT_VERSION < 0x040700:
|
| 217 |
+
raise ImportError("IPython requires PyQt4 >= 4.7, found %s" %
|
| 218 |
+
QtCore.PYQT_VERSION_STR)
|
| 219 |
+
|
| 220 |
+
# Alias PyQt-specific functions for PySide compatibility.
|
| 221 |
+
QtCore.Signal = QtCore.pyqtSignal
|
| 222 |
+
QtCore.Slot = QtCore.pyqtSlot
|
| 223 |
+
|
| 224 |
+
# query for the API version (in case version == None)
|
| 225 |
+
version = sip.getapi('QString')
|
| 226 |
+
api = QT_API_PYQTv1 if version == 1 else QT_API_PYQT
|
| 227 |
+
return QtCore, QtGui, QtSvg, api
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
def import_pyqt5():
|
| 231 |
+
"""
|
| 232 |
+
Import PyQt5
|
| 233 |
+
|
| 234 |
+
ImportErrors raised within this function are non-recoverable
|
| 235 |
+
"""
|
| 236 |
+
|
| 237 |
+
from PyQt5 import QtCore, QtSvg, QtWidgets, QtGui
|
| 238 |
+
|
| 239 |
+
# Alias PyQt-specific functions for PySide compatibility.
|
| 240 |
+
QtCore.Signal = QtCore.pyqtSignal
|
| 241 |
+
QtCore.Slot = QtCore.pyqtSlot
|
| 242 |
+
|
| 243 |
+
# Join QtGui and QtWidgets for Qt4 compatibility.
|
| 244 |
+
QtGuiCompat = types.ModuleType('QtGuiCompat')
|
| 245 |
+
QtGuiCompat.__dict__.update(QtGui.__dict__)
|
| 246 |
+
QtGuiCompat.__dict__.update(QtWidgets.__dict__)
|
| 247 |
+
|
| 248 |
+
api = QT_API_PYQT5
|
| 249 |
+
return QtCore, QtGuiCompat, QtSvg, api
|
| 250 |
+
|
| 251 |
+
|
| 252 |
+
def import_pyqt6():
|
| 253 |
+
"""
|
| 254 |
+
Import PyQt6
|
| 255 |
+
|
| 256 |
+
ImportErrors raised within this function are non-recoverable
|
| 257 |
+
"""
|
| 258 |
+
|
| 259 |
+
from PyQt6 import QtCore, QtSvg, QtWidgets, QtGui
|
| 260 |
+
|
| 261 |
+
# Alias PyQt-specific functions for PySide compatibility.
|
| 262 |
+
QtCore.Signal = QtCore.pyqtSignal
|
| 263 |
+
QtCore.Slot = QtCore.pyqtSlot
|
| 264 |
+
|
| 265 |
+
# Join QtGui and QtWidgets for Qt4 compatibility.
|
| 266 |
+
QtGuiCompat = types.ModuleType("QtGuiCompat")
|
| 267 |
+
QtGuiCompat.__dict__.update(QtGui.__dict__)
|
| 268 |
+
QtGuiCompat.__dict__.update(QtWidgets.__dict__)
|
| 269 |
+
|
| 270 |
+
api = QT_API_PYQT6
|
| 271 |
+
return QtCore, QtGuiCompat, QtSvg, api
|
| 272 |
+
|
| 273 |
+
|
| 274 |
+
def import_pyside():
|
| 275 |
+
"""
|
| 276 |
+
Import PySide
|
| 277 |
+
|
| 278 |
+
ImportErrors raised within this function are non-recoverable
|
| 279 |
+
"""
|
| 280 |
+
from PySide import QtGui, QtCore, QtSvg
|
| 281 |
+
return QtCore, QtGui, QtSvg, QT_API_PYSIDE
|
| 282 |
+
|
| 283 |
+
def import_pyside2():
|
| 284 |
+
"""
|
| 285 |
+
Import PySide2
|
| 286 |
+
|
| 287 |
+
ImportErrors raised within this function are non-recoverable
|
| 288 |
+
"""
|
| 289 |
+
from PySide2 import QtGui, QtCore, QtSvg, QtWidgets, QtPrintSupport
|
| 290 |
+
|
| 291 |
+
# Join QtGui and QtWidgets for Qt4 compatibility.
|
| 292 |
+
QtGuiCompat = types.ModuleType('QtGuiCompat')
|
| 293 |
+
QtGuiCompat.__dict__.update(QtGui.__dict__)
|
| 294 |
+
QtGuiCompat.__dict__.update(QtWidgets.__dict__)
|
| 295 |
+
QtGuiCompat.__dict__.update(QtPrintSupport.__dict__)
|
| 296 |
+
|
| 297 |
+
return QtCore, QtGuiCompat, QtSvg, QT_API_PYSIDE2
|
| 298 |
+
|
| 299 |
+
|
| 300 |
+
def import_pyside6():
|
| 301 |
+
"""
|
| 302 |
+
Import PySide6
|
| 303 |
+
|
| 304 |
+
ImportErrors raised within this function are non-recoverable
|
| 305 |
+
"""
|
| 306 |
+
|
| 307 |
+
def get_attrs(module):
|
| 308 |
+
return {
|
| 309 |
+
name: getattr(module, name)
|
| 310 |
+
for name in dir(module)
|
| 311 |
+
if not name.startswith("_")
|
| 312 |
+
}
|
| 313 |
+
|
| 314 |
+
from PySide6 import QtGui, QtCore, QtSvg, QtWidgets, QtPrintSupport
|
| 315 |
+
|
| 316 |
+
# Join QtGui and QtWidgets for Qt4 compatibility.
|
| 317 |
+
QtGuiCompat = types.ModuleType("QtGuiCompat")
|
| 318 |
+
QtGuiCompat.__dict__.update(QtGui.__dict__)
|
| 319 |
+
if QtCore.__version_info__ < (6, 7):
|
| 320 |
+
QtGuiCompat.__dict__.update(QtWidgets.__dict__)
|
| 321 |
+
QtGuiCompat.__dict__.update(QtPrintSupport.__dict__)
|
| 322 |
+
else:
|
| 323 |
+
QtGuiCompat.__dict__.update(get_attrs(QtWidgets))
|
| 324 |
+
QtGuiCompat.__dict__.update(get_attrs(QtPrintSupport))
|
| 325 |
+
|
| 326 |
+
return QtCore, QtGuiCompat, QtSvg, QT_API_PYSIDE6
|
| 327 |
+
|
| 328 |
+
|
| 329 |
+
def load_qt(api_options):
|
| 330 |
+
"""
|
| 331 |
+
Attempt to import Qt, given a preference list
|
| 332 |
+
of permissible bindings
|
| 333 |
+
|
| 334 |
+
It is safe to call this function multiple times.
|
| 335 |
+
|
| 336 |
+
Parameters
|
| 337 |
+
----------
|
| 338 |
+
api_options : List of strings
|
| 339 |
+
The order of APIs to try. Valid items are 'pyside', 'pyside2',
|
| 340 |
+
'pyqt', 'pyqt5', 'pyqtv1' and 'pyqtdefault'
|
| 341 |
+
|
| 342 |
+
Returns
|
| 343 |
+
-------
|
| 344 |
+
A tuple of QtCore, QtGui, QtSvg, QT_API
|
| 345 |
+
The first three are the Qt modules. The last is the
|
| 346 |
+
string indicating which module was loaded.
|
| 347 |
+
|
| 348 |
+
Raises
|
| 349 |
+
------
|
| 350 |
+
ImportError, if it isn't possible to import any requested
|
| 351 |
+
bindings (either because they aren't installed, or because
|
| 352 |
+
an incompatible library has already been installed)
|
| 353 |
+
"""
|
| 354 |
+
loaders = {
|
| 355 |
+
# Qt6
|
| 356 |
+
QT_API_PYQT6: import_pyqt6,
|
| 357 |
+
QT_API_PYSIDE6: import_pyside6,
|
| 358 |
+
# Qt5
|
| 359 |
+
QT_API_PYQT5: import_pyqt5,
|
| 360 |
+
QT_API_PYSIDE2: import_pyside2,
|
| 361 |
+
# Qt4
|
| 362 |
+
QT_API_PYSIDE: import_pyside,
|
| 363 |
+
QT_API_PYQT: import_pyqt4,
|
| 364 |
+
QT_API_PYQTv1: partial(import_pyqt4, version=1),
|
| 365 |
+
# default
|
| 366 |
+
QT_API_PYQT_DEFAULT: import_pyqt6,
|
| 367 |
+
}
|
| 368 |
+
|
| 369 |
+
for api in api_options:
|
| 370 |
+
|
| 371 |
+
if api not in loaders:
|
| 372 |
+
raise RuntimeError(
|
| 373 |
+
"Invalid Qt API %r, valid values are: %s" %
|
| 374 |
+
(api, ", ".join(["%r" % k for k in loaders.keys()])))
|
| 375 |
+
|
| 376 |
+
if not can_import(api):
|
| 377 |
+
continue
|
| 378 |
+
|
| 379 |
+
#cannot safely recover from an ImportError during this
|
| 380 |
+
result = loaders[api]()
|
| 381 |
+
api = result[-1] # changed if api = QT_API_PYQT_DEFAULT
|
| 382 |
+
commit_api(api)
|
| 383 |
+
return result
|
| 384 |
+
else:
|
| 385 |
+
# Clear the environment variable since it doesn't work.
|
| 386 |
+
if "QT_API" in os.environ:
|
| 387 |
+
del os.environ["QT_API"]
|
| 388 |
+
|
| 389 |
+
raise ImportError(
|
| 390 |
+
"""
|
| 391 |
+
Could not load requested Qt binding. Please ensure that
|
| 392 |
+
PyQt4 >= 4.7, PyQt5, PyQt6, PySide >= 1.0.3, PySide2, or
|
| 393 |
+
PySide6 is available, and only one is imported per session.
|
| 394 |
+
|
| 395 |
+
Currently-imported Qt library: %r
|
| 396 |
+
PyQt5 available (requires QtCore, QtGui, QtSvg, QtWidgets): %s
|
| 397 |
+
PyQt6 available (requires QtCore, QtGui, QtSvg, QtWidgets): %s
|
| 398 |
+
PySide2 installed: %s
|
| 399 |
+
PySide6 installed: %s
|
| 400 |
+
Tried to load: %r
|
| 401 |
+
"""
|
| 402 |
+
% (
|
| 403 |
+
loaded_api(),
|
| 404 |
+
has_binding(QT_API_PYQT5),
|
| 405 |
+
has_binding(QT_API_PYQT6),
|
| 406 |
+
has_binding(QT_API_PYSIDE2),
|
| 407 |
+
has_binding(QT_API_PYSIDE6),
|
| 408 |
+
api_options,
|
| 409 |
+
)
|
| 410 |
+
)
|
| 411 |
+
|
| 412 |
+
|
| 413 |
+
def enum_factory(QT_API, QtCore):
|
| 414 |
+
"""Construct an enum helper to account for PyQt5 <-> PyQt6 changes."""
|
| 415 |
+
|
| 416 |
+
@lru_cache(None)
|
| 417 |
+
def _enum(name):
|
| 418 |
+
# foo.bar.Enum.Entry (PyQt6) <=> foo.bar.Entry (non-PyQt6).
|
| 419 |
+
return operator.attrgetter(
|
| 420 |
+
name if QT_API == QT_API_PYQT6 else name.rpartition(".")[0]
|
| 421 |
+
)(sys.modules[QtCore.__package__])
|
| 422 |
+
|
| 423 |
+
return _enum
|
lib/python3.12/site-packages/IPython/utils/PyColorize.py
ADDED
|
@@ -0,0 +1,577 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import keyword
|
| 2 |
+
import os
|
| 3 |
+
import sys
|
| 4 |
+
import token
|
| 5 |
+
import tokenize
|
| 6 |
+
import warnings
|
| 7 |
+
from io import StringIO
|
| 8 |
+
from typing import Any, Type, TypeAlias
|
| 9 |
+
|
| 10 |
+
import pygments
|
| 11 |
+
from pygments.formatters.terminal256 import Terminal256Formatter
|
| 12 |
+
from pygments.style import Style
|
| 13 |
+
from pygments.styles import get_style_by_name
|
| 14 |
+
from pygments.token import Token, _TokenType
|
| 15 |
+
from functools import cache
|
| 16 |
+
|
| 17 |
+
from typing import TypedDict
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
TokenStream: TypeAlias = list[tuple[_TokenType, str]]
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
__all__ = ["Parser", "Theme"]
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class Symbols(TypedDict):
|
| 27 |
+
top_line: str
|
| 28 |
+
arrow_body: str
|
| 29 |
+
arrow_head: str
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
_default_symbols: Symbols = Symbols(
|
| 33 |
+
top_line="-",
|
| 34 |
+
arrow_body="-",
|
| 35 |
+
arrow_head=">",
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class Theme:
|
| 40 |
+
name: str
|
| 41 |
+
base: str | None
|
| 42 |
+
extra_style: dict[_TokenType, str]
|
| 43 |
+
symbols: Symbols
|
| 44 |
+
|
| 45 |
+
def __init__(
|
| 46 |
+
self,
|
| 47 |
+
name: str,
|
| 48 |
+
base: str | None,
|
| 49 |
+
extra_style: dict[_TokenType, str],
|
| 50 |
+
*,
|
| 51 |
+
symbols: Symbols | None = None,
|
| 52 |
+
) -> None:
|
| 53 |
+
self.name = name
|
| 54 |
+
self.base = base
|
| 55 |
+
self.extra_style = extra_style
|
| 56 |
+
s: Symbols = symbols if symbols is not None else _default_symbols
|
| 57 |
+
self.symbols = {**_default_symbols, **s}
|
| 58 |
+
self._formatter = Terminal256Formatter(style=self.as_pygments_style())
|
| 59 |
+
|
| 60 |
+
@cache
|
| 61 |
+
def as_pygments_style(self) -> Type[Style]:
|
| 62 |
+
if self.base is not None:
|
| 63 |
+
base_styles = get_style_by_name(self.base).styles
|
| 64 |
+
else:
|
| 65 |
+
base_styles = {}
|
| 66 |
+
|
| 67 |
+
class MyStyle(Style):
|
| 68 |
+
styles = {**base_styles, **self.extra_style}
|
| 69 |
+
|
| 70 |
+
return MyStyle
|
| 71 |
+
|
| 72 |
+
def format(self, stream: TokenStream) -> str:
|
| 73 |
+
return pygments.format(stream, self._formatter)
|
| 74 |
+
|
| 75 |
+
def make_arrow(self, width: int) -> str:
|
| 76 |
+
"""generate the leading arrow in front of traceback or debugger"""
|
| 77 |
+
if width >= 2:
|
| 78 |
+
return (
|
| 79 |
+
self.symbols["arrow_body"] * (width - 2)
|
| 80 |
+
+ self.symbols["arrow_head"]
|
| 81 |
+
+ " "
|
| 82 |
+
)
|
| 83 |
+
elif width == 1:
|
| 84 |
+
return self.symbols["arrow_head"]
|
| 85 |
+
return ""
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
generate_tokens = tokenize.generate_tokens
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
#############################################################################
|
| 92 |
+
### Python Source Parser (does Highlighting)
|
| 93 |
+
#############################################################################
|
| 94 |
+
|
| 95 |
+
_KEYWORD = token.NT_OFFSET + 1
|
| 96 |
+
_TEXT = token.NT_OFFSET + 2
|
| 97 |
+
|
| 98 |
+
# ****************************************************************************
|
| 99 |
+
|
| 100 |
+
_pygment_token_mapping: dict[int, _TokenType] = {
|
| 101 |
+
token.NUMBER: Token.Literal.Number,
|
| 102 |
+
token.OP: Token.Operator,
|
| 103 |
+
token.STRING: Token.Literal.String,
|
| 104 |
+
token.COMMENT: Token.Comment,
|
| 105 |
+
token.NAME: Token.Name,
|
| 106 |
+
token.ERRORTOKEN: Token.Error,
|
| 107 |
+
_KEYWORD: Token.Keyword,
|
| 108 |
+
_TEXT: Token.Text,
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
# technically BW is not nocolor, we should have a no-style, style
|
| 112 |
+
nocolors_theme = Theme("nocolor", None, {})
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
linux_theme = Theme(
|
| 116 |
+
"linux",
|
| 117 |
+
"monokai",
|
| 118 |
+
{
|
| 119 |
+
Token.Header: "ansibrightred",
|
| 120 |
+
Token.LinenoEm: "ansibrightgreen",
|
| 121 |
+
Token.Lineno: "ansigreen",
|
| 122 |
+
Token.ValEm: "ansibrightblue",
|
| 123 |
+
Token.VName: "ansicyan",
|
| 124 |
+
Token.Caret: "",
|
| 125 |
+
Token.Filename: "ansibrightgreen",
|
| 126 |
+
Token.ExcName: "ansibrightred",
|
| 127 |
+
Token.Topline: "ansibrightred",
|
| 128 |
+
Token.FilenameEm: "ansigreen",
|
| 129 |
+
Token.Normal: "",
|
| 130 |
+
Token.NormalEm: "ansibrightcyan",
|
| 131 |
+
Token.Line: "ansiyellow",
|
| 132 |
+
Token.TB.Name: "ansimagenta",
|
| 133 |
+
Token.TB.NameEm: "ansibrightmagenta",
|
| 134 |
+
Token.Breakpoint: "",
|
| 135 |
+
Token.Breakpoint.Enabled: "ansibrightred",
|
| 136 |
+
Token.Breakpoint.Disabled: "ansired",
|
| 137 |
+
Token.Prompt: "ansibrightgreen",
|
| 138 |
+
Token.PromptNum: "ansigreen bold",
|
| 139 |
+
Token.OutPrompt: "ansibrightred",
|
| 140 |
+
Token.OutPromptNum: "ansired bold",
|
| 141 |
+
},
|
| 142 |
+
)
|
| 143 |
+
|
| 144 |
+
neutral_pygments_equiv = {
|
| 145 |
+
Token.Header: "ansired",
|
| 146 |
+
Token.LinenoEm: "ansigreen",
|
| 147 |
+
Token.Lineno: "ansibrightgreen",
|
| 148 |
+
Token.ValEm: "ansiblue",
|
| 149 |
+
Token.VName: "ansicyan",
|
| 150 |
+
Token.Caret: "",
|
| 151 |
+
Token.Filename: "ansibrightgreen",
|
| 152 |
+
Token.FilenameEm: "ansigreen",
|
| 153 |
+
Token.ExcName: "ansired",
|
| 154 |
+
Token.Topline: "ansired",
|
| 155 |
+
Token.Normal: "",
|
| 156 |
+
Token.NormalEm: "ansicyan",
|
| 157 |
+
Token.Line: "ansired",
|
| 158 |
+
Token.TB.Name: "ansibrightmagenta",
|
| 159 |
+
Token.TB.NameEm: "ansimagenta",
|
| 160 |
+
Token.Breakpoint: "",
|
| 161 |
+
Token.Breakpoint.Enabled: "ansibrightred",
|
| 162 |
+
Token.Breakpoint.Disabled: "ansired",
|
| 163 |
+
## specific override of pygments defaults for visibility
|
| 164 |
+
Token.Number: "ansigreen",
|
| 165 |
+
Token.Operator: "noinherit",
|
| 166 |
+
Token.String: "ansiyellow",
|
| 167 |
+
Token.Name.Function: "ansiblue",
|
| 168 |
+
Token.Name.Class: "bold ansiblue",
|
| 169 |
+
Token.Name.Namespace: "bold ansiblue",
|
| 170 |
+
Token.Name.Variable.Magic: "ansiblue",
|
| 171 |
+
Token.Prompt: "ansigreen",
|
| 172 |
+
Token.OutPrompt: "ansired",
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
neutral_pygments_nt = {
|
| 177 |
+
**neutral_pygments_equiv,
|
| 178 |
+
Token.PromptNum: "ansigreen bold",
|
| 179 |
+
Token.OutPromptNum: "ansired bold",
|
| 180 |
+
}
|
| 181 |
+
neutral_pygments_posix = {
|
| 182 |
+
**neutral_pygments_equiv,
|
| 183 |
+
Token.PromptNum: "ansibrightgreen bold",
|
| 184 |
+
Token.OutPromptNum: "ansibrightred bold",
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
neutral_nt = Theme("neutral:nt", "default", neutral_pygments_nt)
|
| 189 |
+
neutral_posix = Theme("neutral:posix", "default", neutral_pygments_posix)
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
# Hack: the 'neutral' colours are not very visible on a dark background on
|
| 193 |
+
# Windows. Since Windows command prompts have a dark background by default, and
|
| 194 |
+
# relatively few users are likely to alter that, we will use the 'Linux' colours,
|
| 195 |
+
# designed for a dark background, as the default on Windows. Changing it here
|
| 196 |
+
# avoids affecting the prompt colours rendered by prompt_toolkit, where the
|
| 197 |
+
# neutral defaults do work OK.
|
| 198 |
+
if os.name == "nt":
|
| 199 |
+
neutral_theme = neutral_nt
|
| 200 |
+
else:
|
| 201 |
+
neutral_theme = neutral_posix
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
lightbg_theme = Theme(
|
| 205 |
+
"lightbg",
|
| 206 |
+
"pastie",
|
| 207 |
+
{
|
| 208 |
+
Token.Header: "ansired",
|
| 209 |
+
Token.LinenoEm: "ansigreen",
|
| 210 |
+
Token.Lineno: "ansibrightgreen",
|
| 211 |
+
Token.ValEm: "ansiblue",
|
| 212 |
+
Token.VName: "ansicyan",
|
| 213 |
+
Token.Caret: "",
|
| 214 |
+
Token.Filename: "ansigreen",
|
| 215 |
+
Token.FilenameEm: "ansibrightgreen",
|
| 216 |
+
Token.ExcName: "ansired",
|
| 217 |
+
Token.Topline: "ansired",
|
| 218 |
+
Token.Normal: "",
|
| 219 |
+
Token.NormalEm: "ansicyan",
|
| 220 |
+
Token.Line: "ansired",
|
| 221 |
+
Token.TB.Name: "ansibrightmagenta",
|
| 222 |
+
Token.TB.NameEm: "ansimagenta",
|
| 223 |
+
Token.Breakpoint: "",
|
| 224 |
+
Token.Breakpoint.Enabled: "ansibrightred",
|
| 225 |
+
Token.Breakpoint.Disabled: "ansired",
|
| 226 |
+
Token.Prompt: "ansibrightblue",
|
| 227 |
+
Token.PromptNum: "ansiblue bold",
|
| 228 |
+
Token.OutPrompt: "ansibrightred",
|
| 229 |
+
Token.OutPromptNum: "ansired bold",
|
| 230 |
+
},
|
| 231 |
+
)
|
| 232 |
+
|
| 233 |
+
PRIDE_RED = "#E40303"
|
| 234 |
+
PRIDE_ORANGE = "#FF8C00"
|
| 235 |
+
PRIDE_YELLOW = "#FFED00"
|
| 236 |
+
PRIDE_GREEN = "#008026"
|
| 237 |
+
PRIDE_INDIGO = "#004CFF"
|
| 238 |
+
PRIDE_VIOLET = "#732982"
|
| 239 |
+
pride_theme = Theme(
|
| 240 |
+
"pride",
|
| 241 |
+
"pastie",
|
| 242 |
+
{
|
| 243 |
+
Token.Header: PRIDE_INDIGO,
|
| 244 |
+
Token.LinenoEm: f"{PRIDE_GREEN} italic",
|
| 245 |
+
Token.Lineno: f"{PRIDE_GREEN} bold",
|
| 246 |
+
Token.ValEm: f"{PRIDE_INDIGO} italic",
|
| 247 |
+
Token.VName: "ansicyan",
|
| 248 |
+
Token.Caret: "",
|
| 249 |
+
Token.Filename: f"{PRIDE_YELLOW}",
|
| 250 |
+
Token.FilenameEm: f"bg:{PRIDE_VIOLET}",
|
| 251 |
+
Token.ExcName: f"{PRIDE_ORANGE}",
|
| 252 |
+
Token.Topline: f"{PRIDE_RED}",
|
| 253 |
+
Token.Normal: "",
|
| 254 |
+
Token.NormalEm: "bold",
|
| 255 |
+
Token.Line: "ansired",
|
| 256 |
+
Token.TB.Name: "ansibrightmagenta",
|
| 257 |
+
Token.TB.NameEm: "ansimagenta",
|
| 258 |
+
Token.Breakpoint: "",
|
| 259 |
+
Token.Breakpoint.Enabled: "ansibrightred",
|
| 260 |
+
Token.Breakpoint.Disabled: "ansired",
|
| 261 |
+
Token.Prompt: "ansibrightblue",
|
| 262 |
+
Token.Prompt.Continuation.L1: f"ansiwhite bg:{PRIDE_RED}",
|
| 263 |
+
Token.Prompt.Continuation.L2: f"ansiwhite bg:{PRIDE_ORANGE}",
|
| 264 |
+
Token.Prompt.Continuation.L3: f"ansiblack bg:{PRIDE_YELLOW}",
|
| 265 |
+
Token.Prompt.Continuation.L4: f"ansiwhite bg:{PRIDE_GREEN}",
|
| 266 |
+
Token.Prompt.Continuation.L5: f"ansiwhite bg:{PRIDE_INDIGO}",
|
| 267 |
+
Token.Prompt.Continuation.L6: f"ansiwhite bg:{PRIDE_VIOLET}",
|
| 268 |
+
Token.PromptNum: "ansiblue bold",
|
| 269 |
+
Token.OutPrompt: "ansibrightred",
|
| 270 |
+
Token.OutPromptNum: "ansired bold",
|
| 271 |
+
},
|
| 272 |
+
symbols={"arrow_body": "\u2500", "arrow_head": "\u25b6", "top_line": "\u2500"},
|
| 273 |
+
)
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
C1 = "#D52D00"
|
| 277 |
+
C2 = "#EF7627"
|
| 278 |
+
C3 = "#FF9A56"
|
| 279 |
+
White = "#FFFFFF"
|
| 280 |
+
C5 = "#D162A4"
|
| 281 |
+
C6 = "#B55690"
|
| 282 |
+
C7 = "#A30262"
|
| 283 |
+
|
| 284 |
+
pl = {
|
| 285 |
+
# Token.Whitespace: "#bbbbbb",
|
| 286 |
+
Token.Comment: "#888888",
|
| 287 |
+
Token.String: C5,
|
| 288 |
+
Token.String.Escape: C1,
|
| 289 |
+
Token.Keyword: f"italic {C2}",
|
| 290 |
+
Token.Name.Class: C2,
|
| 291 |
+
Token.Name.Exception: C1,
|
| 292 |
+
Token.Name.Builtin: C3,
|
| 293 |
+
Token.Name.Variable: C6,
|
| 294 |
+
Token.Name.Constant: C7,
|
| 295 |
+
Token.Name.Decorator: C2,
|
| 296 |
+
Token.Number: C7,
|
| 297 |
+
Token.Generic.Deleted: f"bg:{C1} #000000",
|
| 298 |
+
Token.Generic.Emph: "italic",
|
| 299 |
+
Token.Generic.Strong: "bold",
|
| 300 |
+
Token.Generic.EmphStrong: "bold italic",
|
| 301 |
+
}
|
| 302 |
+
|
| 303 |
+
pridel_theme = Theme(
|
| 304 |
+
"pride:l",
|
| 305 |
+
None,
|
| 306 |
+
{
|
| 307 |
+
Token.Header: C3,
|
| 308 |
+
Token.LinenoEm: C3,
|
| 309 |
+
Token.Lineno: C2,
|
| 310 |
+
Token.ValEm: C2,
|
| 311 |
+
Token.VName: C2,
|
| 312 |
+
Token.Caret: "",
|
| 313 |
+
Token.Filename: C2,
|
| 314 |
+
Token.FilenameEm: C3,
|
| 315 |
+
Token.ExcName: C1,
|
| 316 |
+
Token.Topline: C1,
|
| 317 |
+
Token.Normal: "",
|
| 318 |
+
Token.NormalEm: "bold",
|
| 319 |
+
Token.Line: C2,
|
| 320 |
+
Token.TB.Name: C6,
|
| 321 |
+
Token.TB.NameEm: C7,
|
| 322 |
+
Token.Breakpoint: "",
|
| 323 |
+
Token.Breakpoint.Enabled: C1,
|
| 324 |
+
Token.Breakpoint.Disabled: C7,
|
| 325 |
+
Token.Prompt: C1,
|
| 326 |
+
Token.PromptNum: C2,
|
| 327 |
+
Token.Prompt.Continuation: C7,
|
| 328 |
+
Token.Prompt.Continuation.L1: C2,
|
| 329 |
+
Token.Prompt.Continuation.L2: C3,
|
| 330 |
+
Token.Prompt.Continuation.L3: White,
|
| 331 |
+
Token.Prompt.Continuation.L4: C5,
|
| 332 |
+
Token.Prompt.Continuation.L5: C6,
|
| 333 |
+
Token.Prompt.Continuation.L6: C7,
|
| 334 |
+
Token.OutPrompt: C6,
|
| 335 |
+
Token.OutPromptNum: C5,
|
| 336 |
+
**pl,
|
| 337 |
+
},
|
| 338 |
+
symbols={"arrow_body": "\u2500", "arrow_head": "\u25b6", "top_line": "\u2500"},
|
| 339 |
+
)
|
| 340 |
+
|
| 341 |
+
GRUVBOX_VAL_EM = "#D79921"
|
| 342 |
+
GRUVBOX_V_NAME = "#83A598"
|
| 343 |
+
GRUVBOX_FILENAME = "#FBF1C7"
|
| 344 |
+
GRUVBOX_EXCEPTION_NAME = "#FB4934"
|
| 345 |
+
GRUVBOX_TOPLINE = "#CC241D"
|
| 346 |
+
GRUVBOX_BREAKPOINT_ENABLED = "#FB4934"
|
| 347 |
+
GRUVBOX_BREAKPOINT_DISABLED = "#CC241D"
|
| 348 |
+
GRUVBOX_PROMPT = "#689D6A"
|
| 349 |
+
GRUVBOX_PROMPT_NUM = "#8EC07C"
|
| 350 |
+
GRUVBOX_OUT_PROMPT = "#B16286"
|
| 351 |
+
GRUVBOX_OUT_PROMPT_NUM = "#D3869B"
|
| 352 |
+
gruvbox_dark_theme = Theme(
|
| 353 |
+
"gruvbox-dark",
|
| 354 |
+
"gruvbox-dark",
|
| 355 |
+
{
|
| 356 |
+
Token.Lineno: GRUVBOX_PROMPT_NUM,
|
| 357 |
+
Token.LinenoEm: f"{GRUVBOX_PROMPT_NUM} bold",
|
| 358 |
+
Token.ValEm: f"{GRUVBOX_VAL_EM} bold",
|
| 359 |
+
Token.VName: GRUVBOX_V_NAME,
|
| 360 |
+
Token.Caret: "",
|
| 361 |
+
Token.Filename: GRUVBOX_FILENAME,
|
| 362 |
+
Token.FilenameEm: f"{GRUVBOX_FILENAME} bold",
|
| 363 |
+
Token.ExcName: f"{GRUVBOX_EXCEPTION_NAME} bold",
|
| 364 |
+
Token.Topline: GRUVBOX_TOPLINE,
|
| 365 |
+
Token.Breakpoint.Enabled: GRUVBOX_BREAKPOINT_ENABLED,
|
| 366 |
+
Token.Breakpoint.Disabled: GRUVBOX_BREAKPOINT_DISABLED,
|
| 367 |
+
Token.Prompt: GRUVBOX_PROMPT,
|
| 368 |
+
Token.PromptNum: f"{GRUVBOX_PROMPT_NUM} bold",
|
| 369 |
+
Token.OutPrompt: GRUVBOX_OUT_PROMPT,
|
| 370 |
+
Token.OutPromptNum: f"{GRUVBOX_OUT_PROMPT_NUM} bold",
|
| 371 |
+
},
|
| 372 |
+
symbols={"arrow_body": "\u2500", "arrow_head": "\u25b6", "top_line": "\u2500"},
|
| 373 |
+
)
|
| 374 |
+
|
| 375 |
+
theme_table: dict[str, Theme] = {
|
| 376 |
+
"nocolor": nocolors_theme,
|
| 377 |
+
"linux": linux_theme,
|
| 378 |
+
"neutral": neutral_theme,
|
| 379 |
+
"neutral:nt": neutral_nt,
|
| 380 |
+
"neutral:posix": neutral_posix,
|
| 381 |
+
"lightbg": lightbg_theme,
|
| 382 |
+
"pride": pride_theme,
|
| 383 |
+
"pride:l": pridel_theme,
|
| 384 |
+
"gruvbox-dark": gruvbox_dark_theme,
|
| 385 |
+
}
|
| 386 |
+
|
| 387 |
+
|
| 388 |
+
class Parser:
|
| 389 |
+
"""Format colored Python source."""
|
| 390 |
+
|
| 391 |
+
_theme_name: str
|
| 392 |
+
out: Any
|
| 393 |
+
pos: int
|
| 394 |
+
lines: list[int]
|
| 395 |
+
raw: str
|
| 396 |
+
|
| 397 |
+
def __init__(self, out: Any = sys.stdout, *, theme_name: str | None = None) -> None:
|
| 398 |
+
"""Create a parser with a specified color table and output channel.
|
| 399 |
+
|
| 400 |
+
Call format() to process code.
|
| 401 |
+
"""
|
| 402 |
+
|
| 403 |
+
assert theme_name is not None
|
| 404 |
+
|
| 405 |
+
self.out = out
|
| 406 |
+
self.pos = 0
|
| 407 |
+
self.lines = []
|
| 408 |
+
self.raw = ""
|
| 409 |
+
if theme_name is not None:
|
| 410 |
+
if theme_name in ["Linux", "LightBG", "Neutral", "NoColor"]:
|
| 411 |
+
warnings.warn(
|
| 412 |
+
f"Theme names and color schemes are lowercase in IPython 9.0 use {theme_name.lower()} instead",
|
| 413 |
+
DeprecationWarning,
|
| 414 |
+
stacklevel=2,
|
| 415 |
+
)
|
| 416 |
+
theme_name = theme_name.lower()
|
| 417 |
+
if not theme_name:
|
| 418 |
+
self.theme_name = "nocolor"
|
| 419 |
+
else:
|
| 420 |
+
self.theme_name = theme_name
|
| 421 |
+
|
| 422 |
+
@property
|
| 423 |
+
def theme_name(self) -> str:
|
| 424 |
+
return self._theme_name
|
| 425 |
+
|
| 426 |
+
@theme_name.setter
|
| 427 |
+
def theme_name(self, value: str) -> None:
|
| 428 |
+
assert value == value.lower()
|
| 429 |
+
self._theme_name = value
|
| 430 |
+
|
| 431 |
+
@property
|
| 432 |
+
def style(self) -> str:
|
| 433 |
+
assert False
|
| 434 |
+
return self._theme_name
|
| 435 |
+
|
| 436 |
+
@style.setter
|
| 437 |
+
def style(self, val: str) -> None:
|
| 438 |
+
assert False
|
| 439 |
+
assert val == val.lower()
|
| 440 |
+
self._theme_name = val
|
| 441 |
+
|
| 442 |
+
def format(self, raw: str, out: Any = None) -> str | None:
|
| 443 |
+
return self.format2(raw, out)[0]
|
| 444 |
+
|
| 445 |
+
def format2(self, raw: str, out: Any = None) -> tuple[str | None, bool]:
|
| 446 |
+
"""Parse and send the colored source.
|
| 447 |
+
|
| 448 |
+
If out is not specified, the defaults (given to constructor) are used.
|
| 449 |
+
|
| 450 |
+
out should be a file-type object. Optionally, out can be given as the
|
| 451 |
+
string 'str' and the parser will automatically return the output in a
|
| 452 |
+
string."""
|
| 453 |
+
|
| 454 |
+
string_output = 0
|
| 455 |
+
if out == "str" or self.out == "str" or isinstance(self.out, StringIO):
|
| 456 |
+
# XXX - I don't really like this state handling logic, but at this
|
| 457 |
+
# point I don't want to make major changes, so adding the
|
| 458 |
+
# isinstance() check is the simplest I can do to ensure correct
|
| 459 |
+
# behavior.
|
| 460 |
+
out_old = self.out
|
| 461 |
+
self.out = StringIO()
|
| 462 |
+
string_output = 1
|
| 463 |
+
elif out is not None:
|
| 464 |
+
self.out = out
|
| 465 |
+
else:
|
| 466 |
+
raise ValueError(
|
| 467 |
+
'`out` or `self.out` should be file-like or the value `"str"`'
|
| 468 |
+
)
|
| 469 |
+
|
| 470 |
+
# Fast return of the unmodified input for nocolor scheme
|
| 471 |
+
# TODO:
|
| 472 |
+
if self.theme_name == "nocolor":
|
| 473 |
+
error = False
|
| 474 |
+
self.out.write(raw)
|
| 475 |
+
if string_output:
|
| 476 |
+
return raw, error
|
| 477 |
+
return None, error
|
| 478 |
+
|
| 479 |
+
# local shorthands
|
| 480 |
+
|
| 481 |
+
# Remove trailing whitespace and normalize tabs
|
| 482 |
+
self.raw = raw.expandtabs().rstrip()
|
| 483 |
+
|
| 484 |
+
# store line offsets in self.lines
|
| 485 |
+
self.lines = [0, 0]
|
| 486 |
+
pos = 0
|
| 487 |
+
raw_find = self.raw.find
|
| 488 |
+
lines_append = self.lines.append
|
| 489 |
+
while True:
|
| 490 |
+
pos = raw_find("\n", pos) + 1
|
| 491 |
+
if not pos:
|
| 492 |
+
break
|
| 493 |
+
lines_append(pos)
|
| 494 |
+
lines_append(len(self.raw))
|
| 495 |
+
|
| 496 |
+
# parse the source and write it
|
| 497 |
+
self.pos = 0
|
| 498 |
+
text = StringIO(self.raw)
|
| 499 |
+
|
| 500 |
+
error = False
|
| 501 |
+
try:
|
| 502 |
+
for atoken in generate_tokens(text.readline):
|
| 503 |
+
self(*atoken)
|
| 504 |
+
except tokenize.TokenError as ex:
|
| 505 |
+
msg = ex.args[0]
|
| 506 |
+
line = ex.args[1][0]
|
| 507 |
+
self.out.write(
|
| 508 |
+
theme_table[self.theme_name].format(
|
| 509 |
+
[
|
| 510 |
+
(Token, "\n\n"),
|
| 511 |
+
(
|
| 512 |
+
Token.Error,
|
| 513 |
+
f"*** ERROR: {msg}{self.raw[self.lines[line] :]}",
|
| 514 |
+
),
|
| 515 |
+
(Token, "\n"),
|
| 516 |
+
]
|
| 517 |
+
)
|
| 518 |
+
)
|
| 519 |
+
error = True
|
| 520 |
+
self.out.write(
|
| 521 |
+
theme_table[self.theme_name].format(
|
| 522 |
+
[
|
| 523 |
+
(Token, "\n"),
|
| 524 |
+
]
|
| 525 |
+
)
|
| 526 |
+
)
|
| 527 |
+
|
| 528 |
+
if string_output:
|
| 529 |
+
output = self.out.getvalue()
|
| 530 |
+
self.out = out_old
|
| 531 |
+
return (output, error)
|
| 532 |
+
return (None, error)
|
| 533 |
+
|
| 534 |
+
def _inner_call_(
|
| 535 |
+
self, toktype: int, toktext: str, start_pos: tuple[int, int]
|
| 536 |
+
) -> str:
|
| 537 |
+
"""like call but write to a temporary buffer"""
|
| 538 |
+
srow, scol = start_pos
|
| 539 |
+
|
| 540 |
+
# calculate new positions
|
| 541 |
+
oldpos = self.pos
|
| 542 |
+
newpos = self.lines[srow] + scol
|
| 543 |
+
self.pos = newpos + len(toktext)
|
| 544 |
+
|
| 545 |
+
# send the original whitespace, if needed
|
| 546 |
+
if newpos > oldpos:
|
| 547 |
+
acc = self.raw[oldpos:newpos]
|
| 548 |
+
else:
|
| 549 |
+
acc = ""
|
| 550 |
+
|
| 551 |
+
# skip indenting tokens
|
| 552 |
+
if toktype in [token.INDENT, token.DEDENT]:
|
| 553 |
+
self.pos = newpos
|
| 554 |
+
return acc
|
| 555 |
+
|
| 556 |
+
# map token type to a color group
|
| 557 |
+
if token.LPAR <= toktype <= token.OP:
|
| 558 |
+
toktype = token.OP
|
| 559 |
+
elif toktype == token.NAME and keyword.iskeyword(toktext):
|
| 560 |
+
toktype = _KEYWORD
|
| 561 |
+
pyg_tok_type = _pygment_token_mapping.get(toktype, Token.Text)
|
| 562 |
+
|
| 563 |
+
# send text, pygments should take care of splitting on newline and resending
|
| 564 |
+
# the correct self.colors after the new line, which is necessary for pagers
|
| 565 |
+
acc += theme_table[self.theme_name].format([(pyg_tok_type, toktext)])
|
| 566 |
+
return acc
|
| 567 |
+
|
| 568 |
+
def __call__(
|
| 569 |
+
self,
|
| 570 |
+
toktype: int,
|
| 571 |
+
toktext: str,
|
| 572 |
+
start_pos: tuple[int, int],
|
| 573 |
+
end_pos: tuple[int, int],
|
| 574 |
+
line: str,
|
| 575 |
+
) -> None:
|
| 576 |
+
"""Token handler, with syntax highlighting."""
|
| 577 |
+
self.out.write(self._inner_call_(toktype, toktext, start_pos))
|
lib/python3.12/site-packages/IPython/utils/__init__.py
ADDED
|
File without changes
|
lib/python3.12/site-packages/IPython/utils/__pycache__/PyColorize.cpython-312.pyc
ADDED
|
Binary file (22 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/utils/__pycache__/_process_cli.cpython-312.pyc
ADDED
|
Binary file (2.87 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/utils/__pycache__/_process_common.cpython-312.pyc
ADDED
|
Binary file (8.25 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/utils/__pycache__/_process_emscripten.cpython-312.pyc
ADDED
|
Binary file (850 Bytes). View file
|
|
|
lib/python3.12/site-packages/IPython/utils/__pycache__/_process_posix.cpython-312.pyc
ADDED
|
Binary file (6.27 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/utils/__pycache__/_process_win32.cpython-312.pyc
ADDED
|
Binary file (8.49 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/utils/__pycache__/_process_win32_controller.cpython-312.pyc
ADDED
|
Binary file (21.7 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/utils/__pycache__/_sysinfo.cpython-312.pyc
ADDED
|
Binary file (215 Bytes). View file
|
|
|
lib/python3.12/site-packages/IPython/utils/__pycache__/coloransi.cpython-312.pyc
ADDED
|
Binary file (408 Bytes). View file
|
|
|
lib/python3.12/site-packages/IPython/utils/__pycache__/contexts.cpython-312.pyc
ADDED
|
Binary file (2.22 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/utils/__pycache__/data.cpython-312.pyc
ADDED
|
Binary file (1.97 kB). View file
|
|
|
lib/python3.12/site-packages/IPython/utils/__pycache__/docs.cpython-312.pyc
ADDED
|
Binary file (354 Bytes). View file
|
|
|
lib/python3.12/site-packages/IPython/utils/__pycache__/eventful.cpython-312.pyc
ADDED
|
Binary file (376 Bytes). View file
|
|
|