pyflyby 1.9.3__tar.gz → 1.9.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyflyby might be problematic. Click here for more details.
- {pyflyby-1.9.3/lib/python/pyflyby.egg-info → pyflyby-1.9.5}/PKG-INFO +37 -3
- {pyflyby-1.9.3 → pyflyby-1.9.5}/README.rst +31 -1
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/autopython +1 -1
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/create-imports +1 -1
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/__init__.py +4 -1
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_autoimp.py +130 -35
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_cmdline.py +8 -5
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_comms.py +3 -1
- pyflyby-1.9.5/bin/pyflyby/_dynimp.py +152 -0
- {pyflyby-1.9.3/lib/python → pyflyby-1.9.5/bin}/pyflyby/_file.py +41 -22
- {pyflyby-1.9.3/lib/python → pyflyby-1.9.5/bin}/pyflyby/_flags.py +1 -1
- pyflyby-1.9.5/bin/pyflyby/_import_sorting.py +165 -0
- {pyflyby-1.9.3/lib/python → pyflyby-1.9.5/bin}/pyflyby/_importclns.py +51 -21
- {pyflyby-1.9.3/lib/python → pyflyby-1.9.5/bin}/pyflyby/_importdb.py +134 -44
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_imports2s.py +54 -84
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_importstmt.py +52 -11
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_interactive.py +24 -147
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_livepatch.py +4 -1
- {pyflyby-1.9.3/lib/python → pyflyby-1.9.5/bin}/pyflyby/_modules.py +20 -5
- {pyflyby-1.9.3/lib/python → pyflyby-1.9.5/bin}/pyflyby/_parse.py +267 -242
- {pyflyby-1.9.3/lib/python → pyflyby-1.9.5/bin}/pyflyby/_py.py +12 -22
- {pyflyby-1.9.3/lib/python → pyflyby-1.9.5/bin}/pyflyby/_version.py +1 -2
- {pyflyby-1.9.3/lib/python → pyflyby-1.9.5/bin}/pyflyby/autoimport.py +1 -1
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby-diff +1 -1
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/tidy-imports +37 -14
- {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/__init__.py +4 -1
- {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_autoimp.py +130 -35
- {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_cmdline.py +8 -5
- {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_comms.py +3 -1
- pyflyby-1.9.5/lib/python/pyflyby/_dynimp.py +152 -0
- {pyflyby-1.9.3/bin → pyflyby-1.9.5/lib/python}/pyflyby/_file.py +41 -22
- {pyflyby-1.9.3/bin → pyflyby-1.9.5/lib/python}/pyflyby/_flags.py +1 -1
- pyflyby-1.9.5/lib/python/pyflyby/_import_sorting.py +165 -0
- {pyflyby-1.9.3/bin → pyflyby-1.9.5/lib/python}/pyflyby/_importclns.py +51 -21
- {pyflyby-1.9.3/bin → pyflyby-1.9.5/lib/python}/pyflyby/_importdb.py +134 -44
- {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_imports2s.py +54 -84
- {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_importstmt.py +52 -11
- {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_interactive.py +24 -147
- {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_livepatch.py +4 -1
- {pyflyby-1.9.3/bin → pyflyby-1.9.5/lib/python}/pyflyby/_modules.py +20 -5
- {pyflyby-1.9.3/bin → pyflyby-1.9.5/lib/python}/pyflyby/_parse.py +267 -242
- {pyflyby-1.9.3/bin → pyflyby-1.9.5/lib/python}/pyflyby/_py.py +12 -22
- {pyflyby-1.9.3/bin → pyflyby-1.9.5/lib/python}/pyflyby/_version.py +1 -2
- {pyflyby-1.9.3/bin → pyflyby-1.9.5/lib/python}/pyflyby/autoimport.py +1 -1
- {pyflyby-1.9.3 → pyflyby-1.9.5/lib/python/pyflyby.egg-info}/PKG-INFO +37 -3
- {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby.egg-info/SOURCES.txt +10 -0
- pyflyby-1.9.5/lib/python/pyflyby.egg-info/requires.txt +6 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/libexec/pyflyby/colordiff +1 -1
- pyflyby-1.9.5/pyproject.toml +26 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/setup.py +7 -2
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_autoimp.py +110 -6
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_cmdline.py +46 -14
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_importclns.py +6 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_imports2s.py +3 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_interactive.py +29 -17
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_jupyterlab_pyflyby.py +2 -1
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_livepatch.py +1 -1
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_parse.py +128 -45
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_py.py +228 -226
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/tests_sorts.py +65 -12
- pyflyby-1.9.3/lib/python/pyflyby.egg-info/requires.txt +0 -4
- {pyflyby-1.9.3 → pyflyby-1.9.5}/.pyflyby +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/LICENSE.txt +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/MANIFEST.in +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/autoipython +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/collect-exports +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/collect-imports +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/find-import +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/list-bad-xrefs +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/prune-broken-imports +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/__main__.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_dbg.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_docxref.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_format.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_idents.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_log.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_util.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/importdb.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/reformat-imports +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/replace-star-imports +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/transform-imports +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/LICENSE.txt +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/Makefile +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/TODO.txt +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/__init__.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/api.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/autoimp.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/cmdline.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/comms.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/dbg.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/file.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/flags.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/format.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/idents.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/importclns.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/importdb.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/imports2s.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/importstmt.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/interactive.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/livepatch.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/log.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/modules.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/parse.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/py.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/util.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/autoipython.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/cli.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/collect_exports.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/collect_imports.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/find_import.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/prune_broken_imports.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/py.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/pyflyby_diff.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/reformat_imports.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/replace_star_imports.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/tidy_imports.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/transform_imports.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/conf.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/index.rst +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/make.bat +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/testing.txt +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/etc/pyflyby/canonical.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/etc/pyflyby/common.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/etc/pyflyby/forget.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/etc/pyflyby/mandatory.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/etc/pyflyby/numpy.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/etc/pyflyby/std.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/emacs/pyflyby.el +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/__main__.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_dbg.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_docxref.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_format.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_idents.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_log.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_util.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/importdb.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby.egg-info/dependency_links.txt +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby.egg-info/entry_points.txt +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby.egg-info/top_level.txt +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/libexec/pyflyby/diff-colorize +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/setup.cfg +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/__init__.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_0testconfig.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_docxref.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_file.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_flags.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_format.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_idents.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_importdb.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_importstmt.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_modules.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_util.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/xrefs.py +0 -0
- {pyflyby-1.9.3 → pyflyby-1.9.5}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyflyby
|
|
3
|
-
Version: 1.9.
|
|
3
|
+
Version: 1.9.5
|
|
4
4
|
Summary: pyflyby - Python development productivity tools, in particular automatic import management
|
|
5
5
|
Home-page: https://pypi.org/project/pyflyby/
|
|
6
6
|
Author: Karl Chen
|
|
@@ -16,8 +16,12 @@ Classifier: Topic :: Software Development :: Interpreters
|
|
|
16
16
|
Classifier: Intended Audience :: Developers
|
|
17
17
|
Classifier: License :: OSI Approved :: MIT License
|
|
18
18
|
Classifier: Programming Language :: Python
|
|
19
|
-
Requires-Python: >3.
|
|
19
|
+
Requires-Python: >3.8, <4
|
|
20
20
|
License-File: LICENSE.txt
|
|
21
|
+
Requires-Dist: six
|
|
22
|
+
Requires-Dist: toml
|
|
23
|
+
Requires-Dist: black
|
|
24
|
+
Requires-Dist: typing_extensions>=4.6; python_version < "3.12"
|
|
21
25
|
|
|
22
26
|
#########
|
|
23
27
|
Pyflyby
|
|
@@ -29,7 +33,7 @@ License-File: LICENSE.txt
|
|
|
29
33
|
.. image:: https://travis-ci.org/deshaw/pyflyby.png?branch=master
|
|
30
34
|
:target: https://travis-ci.org/deshaw/pyflyby
|
|
31
35
|
|
|
32
|
-
Pyflyby is a set of Python programming productivity tools for Python 3.
|
|
36
|
+
Pyflyby is a set of Python programming productivity tools for Python 3.8+.
|
|
33
37
|
|
|
34
38
|
For command-line interaction:
|
|
35
39
|
* ``py``: command-line multitool
|
|
@@ -102,6 +106,36 @@ or
|
|
|
102
106
|
[PYFLYBY] from base64 import b64decode
|
|
103
107
|
Out[1]: 'hello'
|
|
104
108
|
|
|
109
|
+
Auto importer lazy variables
|
|
110
|
+
----------------------------
|
|
111
|
+
|
|
112
|
+
It is possible to use the autoimporter to lazily define variables.
|
|
113
|
+
|
|
114
|
+
To use, put the following in your IPython startup files
|
|
115
|
+
(``~/.ipython/profile_default/startup/autoimp.py``), or in your IPython
|
|
116
|
+
configuration file:
|
|
117
|
+
|
|
118
|
+
.. code:: python
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
from pyflyby import add_import
|
|
122
|
+
|
|
123
|
+
add_import("foo", "foo = 1")
|
|
124
|
+
|
|
125
|
+
add_import(
|
|
126
|
+
"df, data as dd",
|
|
127
|
+
'''
|
|
128
|
+
import pandas as pd
|
|
129
|
+
data = [1,2,3]
|
|
130
|
+
df = pd.DataFrame(data)
|
|
131
|
+
''')
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
You can add the keyword ``strict=False`` to not fail if not in IPython or of the
|
|
135
|
+
pyflyby extensions is not loaded.
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
105
139
|
|
|
106
140
|
Quick start: ``py`` command-line multi-tool
|
|
107
141
|
===========================================
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
.. image:: https://travis-ci.org/deshaw/pyflyby.png?branch=master
|
|
9
9
|
:target: https://travis-ci.org/deshaw/pyflyby
|
|
10
10
|
|
|
11
|
-
Pyflyby is a set of Python programming productivity tools for Python 3.
|
|
11
|
+
Pyflyby is a set of Python programming productivity tools for Python 3.8+.
|
|
12
12
|
|
|
13
13
|
For command-line interaction:
|
|
14
14
|
* ``py``: command-line multitool
|
|
@@ -81,6 +81,36 @@ or
|
|
|
81
81
|
[PYFLYBY] from base64 import b64decode
|
|
82
82
|
Out[1]: 'hello'
|
|
83
83
|
|
|
84
|
+
Auto importer lazy variables
|
|
85
|
+
----------------------------
|
|
86
|
+
|
|
87
|
+
It is possible to use the autoimporter to lazily define variables.
|
|
88
|
+
|
|
89
|
+
To use, put the following in your IPython startup files
|
|
90
|
+
(``~/.ipython/profile_default/startup/autoimp.py``), or in your IPython
|
|
91
|
+
configuration file:
|
|
92
|
+
|
|
93
|
+
.. code:: python
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
from pyflyby import add_import
|
|
97
|
+
|
|
98
|
+
add_import("foo", "foo = 1")
|
|
99
|
+
|
|
100
|
+
add_import(
|
|
101
|
+
"df, data as dd",
|
|
102
|
+
'''
|
|
103
|
+
import pandas as pd
|
|
104
|
+
data = [1,2,3]
|
|
105
|
+
df = pd.DataFrame(data)
|
|
106
|
+
''')
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
You can add the keyword ``strict=False`` to not fail if not in IPython or of the
|
|
110
|
+
pyflyby extensions is not loaded.
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
84
114
|
|
|
85
115
|
Quick start: ``py`` command-line multi-tool
|
|
86
116
|
===========================================
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
from pyflyby._autoimp import (auto_eval, auto_import,
|
|
8
8
|
find_missing_imports)
|
|
9
|
+
from pyflyby._dynimp import add_import
|
|
9
10
|
from pyflyby._dbg import (add_debug_functions_to_builtins,
|
|
10
11
|
attach_debugger, debug_on_exception,
|
|
11
12
|
debug_statement, debugger,
|
|
@@ -40,6 +41,8 @@ from pyflyby._dbg import (breakpoint, debug_exception,
|
|
|
40
41
|
enable_signal_handler_breakpoint,
|
|
41
42
|
waitpoint)
|
|
42
43
|
|
|
44
|
+
from typing import Sequence
|
|
45
|
+
|
|
43
46
|
|
|
44
47
|
# Promote the function & classes that we've chosen to expose publicly to be
|
|
45
48
|
# known as pyflyby.Foo instead of pyflyby._module.Foo.
|
|
@@ -51,4 +54,4 @@ del x
|
|
|
51
54
|
|
|
52
55
|
# Discourage "from pyflyby import *".
|
|
53
56
|
# Use the tidy-imports/autoimporter instead!
|
|
54
|
-
__all__ = []
|
|
57
|
+
__all__:Sequence[str] = []
|
|
@@ -6,13 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
import ast
|
|
8
8
|
import builtins
|
|
9
|
+
from collections.abc import Sequence
|
|
9
10
|
import contextlib
|
|
10
11
|
import copy
|
|
11
|
-
from six import reraise
|
|
12
|
-
import sys
|
|
13
|
-
import types
|
|
14
|
-
|
|
15
|
-
from collections.abc import Sequence
|
|
16
12
|
|
|
17
13
|
from pyflyby._file import FileText, Filename
|
|
18
14
|
from pyflyby._flags import CompilerFlags
|
|
@@ -22,7 +18,13 @@ from pyflyby._importdb import ImportDB
|
|
|
22
18
|
from pyflyby._importstmt import Import
|
|
23
19
|
from pyflyby._log import logger
|
|
24
20
|
from pyflyby._modules import ModuleHandle
|
|
25
|
-
from pyflyby._parse import PythonBlock,
|
|
21
|
+
from pyflyby._parse import (PythonBlock, _is_ast_str,
|
|
22
|
+
infer_compile_mode, MatchAs)
|
|
23
|
+
|
|
24
|
+
from six import reraise
|
|
25
|
+
import sys
|
|
26
|
+
import types
|
|
27
|
+
from typing import Any, Set
|
|
26
28
|
|
|
27
29
|
if sys.version_info >= (3, 12):
|
|
28
30
|
ATTRIBUTE_NAME = "value"
|
|
@@ -34,13 +36,13 @@ if sys.version_info > (3, 11):
|
|
|
34
36
|
else:
|
|
35
37
|
LOAD_SHIFT = 0
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
|
|
40
|
+
if sys.version_info >= (3, 10):
|
|
41
|
+
from types import NoneType, EllipsisType
|
|
39
42
|
else:
|
|
40
|
-
|
|
43
|
+
NoneType = type(None)
|
|
44
|
+
EllipsisType = type(Ellipsis)
|
|
41
45
|
|
|
42
|
-
NoneType = type(None)
|
|
43
|
-
EllipsisType = type(Ellipsis)
|
|
44
46
|
|
|
45
47
|
class _ClassScope(dict):
|
|
46
48
|
pass
|
|
@@ -419,7 +421,8 @@ class _MissingImportFinder(object):
|
|
|
419
421
|
|
|
420
422
|
def scan_for_import_issues(self, codeblock):
|
|
421
423
|
# See global `scan_for_import_issues`
|
|
422
|
-
|
|
424
|
+
if not isinstance(codeblock, PythonBlock):
|
|
425
|
+
codeblock = PythonBlock(codeblock)
|
|
423
426
|
node = codeblock.ast_node
|
|
424
427
|
self._scan_node(node)
|
|
425
428
|
# Get missing imports now, before handling docstrings. We don't want
|
|
@@ -488,8 +491,11 @@ class _MissingImportFinder(object):
|
|
|
488
491
|
logger.debug(
|
|
489
492
|
"_MissingImportFinder has no method %r, using generic_visit", method
|
|
490
493
|
)
|
|
491
|
-
|
|
492
|
-
|
|
494
|
+
if hasattr(self, method):
|
|
495
|
+
visitor = getattr(self, method)
|
|
496
|
+
else:
|
|
497
|
+
logger.debug("No method `%s`, using `generic_visit`", method)
|
|
498
|
+
visitor = self.generic_visit
|
|
493
499
|
return visitor(node)
|
|
494
500
|
else:
|
|
495
501
|
raise TypeError("unexpected %s" % (type(node).__name__,))
|
|
@@ -589,7 +595,10 @@ class _MissingImportFinder(object):
|
|
|
589
595
|
logger.warning("Don't know how to handle __all__ with list elements other than str")
|
|
590
596
|
return
|
|
591
597
|
for e in node.value.elts:
|
|
592
|
-
|
|
598
|
+
if sys.version_info > (3,10):
|
|
599
|
+
self._visit_Load_defered_global(e.value)
|
|
600
|
+
else:
|
|
601
|
+
self._visit_Load_defered_global(e.s)
|
|
593
602
|
|
|
594
603
|
def visit_ClassDef(self, node):
|
|
595
604
|
logger.debug("visit_ClassDef(%r)", node)
|
|
@@ -634,6 +643,10 @@ class _MissingImportFinder(object):
|
|
|
634
643
|
else:
|
|
635
644
|
assert node._fields == ('name', 'args', 'body', 'decorator_list', 'returns', 'type_comment'), node._fields
|
|
636
645
|
with self._NewScopeCtx(include_class_scopes=True):
|
|
646
|
+
# we want `__class__` to only be defined in
|
|
647
|
+
# methods and not class body
|
|
648
|
+
if self._in_class_def:
|
|
649
|
+
self.scopestack[-1]["__class__"] = None # we just need to to be defined
|
|
637
650
|
self.visit(node.decorator_list)
|
|
638
651
|
self.visit(node.args)
|
|
639
652
|
if node.returns:
|
|
@@ -817,6 +830,47 @@ class _MissingImportFinder(object):
|
|
|
817
830
|
self._visit_StoreImport(node, modulename)
|
|
818
831
|
self.generic_visit(node)
|
|
819
832
|
|
|
833
|
+
if sys.version_info >= (3,10):
|
|
834
|
+
def visit_match_case(self, node:ast.match_case):
|
|
835
|
+
logger.debug("visit_match_case(%r)", node)
|
|
836
|
+
return self.generic_visit(node)
|
|
837
|
+
|
|
838
|
+
def visit_Match(self, node:ast.Match):
|
|
839
|
+
logger.debug("visit_Match(%r)", node)
|
|
840
|
+
return self.generic_visit(node)
|
|
841
|
+
|
|
842
|
+
def visit_MatchMapping(self, node:ast.MatchMapping):
|
|
843
|
+
logger.debug("visit_MatchMapping(%r)", node)
|
|
844
|
+
return self.generic_visit(node)
|
|
845
|
+
|
|
846
|
+
def visit_MatchAs(self, node:MatchAs):
|
|
847
|
+
logger.debug("visit_MatchAs(%r)", node)
|
|
848
|
+
if node.name is None:
|
|
849
|
+
return
|
|
850
|
+
isinstance(node.name, str), node.name
|
|
851
|
+
return self._visit_Store(node.name)
|
|
852
|
+
|
|
853
|
+
def visit_Call(self, node:ast.Call):
|
|
854
|
+
logger.debug("visit_Call(%r)", node)
|
|
855
|
+
return self.generic_visit(node)
|
|
856
|
+
|
|
857
|
+
def visit_Pass(self, node:ast.Pass):
|
|
858
|
+
logger.debug("visit_Pass(%r)", node)
|
|
859
|
+
return self.generic_visit(node)
|
|
860
|
+
|
|
861
|
+
def visit_Constant(self, node:ast.Constant):
|
|
862
|
+
logger.debug("visit_Constant(%r)", node)
|
|
863
|
+
return self.generic_visit(node)
|
|
864
|
+
|
|
865
|
+
def visit_Module(self, node:ast.Module):
|
|
866
|
+
logger.debug("visit_Module(%r)", node)
|
|
867
|
+
return self.generic_visit(node)
|
|
868
|
+
|
|
869
|
+
def visit_Expr(self, node:ast.Expr):
|
|
870
|
+
logger.debug("visit_Expr(%r)", node)
|
|
871
|
+
return self.generic_visit(node)
|
|
872
|
+
|
|
873
|
+
|
|
820
874
|
def visit_Name(self, node):
|
|
821
875
|
logger.debug("visit_Name(%r)", node.id)
|
|
822
876
|
self._visit_fullname(node.id, node.ctx)
|
|
@@ -874,7 +928,7 @@ class _MissingImportFinder(object):
|
|
|
874
928
|
value = _UseChecker(name, imp, self._lineno)
|
|
875
929
|
self._visit_Store(name, value)
|
|
876
930
|
|
|
877
|
-
def _visit_Store(self, fullname, value=None):
|
|
931
|
+
def _visit_Store(self, fullname:str, value=None):
|
|
878
932
|
logger.debug("_visit_Store(%r)", fullname)
|
|
879
933
|
if fullname is None:
|
|
880
934
|
return
|
|
@@ -1064,7 +1118,8 @@ def scan_for_import_issues(codeblock, find_unused_imports=True, parse_docstrings
|
|
|
1064
1118
|
|
|
1065
1119
|
"""
|
|
1066
1120
|
logger.debug("scan_for_import_issues()")
|
|
1067
|
-
|
|
1121
|
+
if not isinstance(codeblock, PythonBlock):
|
|
1122
|
+
codeblock = PythonBlock(codeblock)
|
|
1068
1123
|
namespaces = ScopeStack([{}])
|
|
1069
1124
|
finder = _MissingImportFinder(namespaces,
|
|
1070
1125
|
find_unused_imports=find_unused_imports,
|
|
@@ -1152,10 +1207,13 @@ def _find_loads_without_stores_in_code(co, loads_without_stores):
|
|
|
1152
1207
|
"_find_loads_without_stores_in_code(): expected a CodeType; got a %s"
|
|
1153
1208
|
% (type(co).__name__,))
|
|
1154
1209
|
# Initialize local constants for fast access.
|
|
1155
|
-
from opcode import
|
|
1210
|
+
from opcode import EXTENDED_ARG, opmap
|
|
1156
1211
|
|
|
1157
1212
|
LOAD_ATTR = opmap['LOAD_ATTR']
|
|
1213
|
+
# LOAD_METHOD is _supposed_ to be removed in 3.12 but still present in opmap
|
|
1214
|
+
# if sys.version_info < (3, 12):
|
|
1158
1215
|
LOAD_METHOD = opmap['LOAD_METHOD']
|
|
1216
|
+
# endif
|
|
1159
1217
|
LOAD_GLOBAL = opmap['LOAD_GLOBAL']
|
|
1160
1218
|
LOAD_NAME = opmap['LOAD_NAME']
|
|
1161
1219
|
STORE_ATTR = opmap['STORE_ATTR']
|
|
@@ -1255,12 +1313,11 @@ def _find_loads_without_stores_in_code(co, loads_without_stores):
|
|
|
1255
1313
|
earliest_backjump_label = _find_earliest_backjump_label(bytecode)
|
|
1256
1314
|
# Loop through bytecode.
|
|
1257
1315
|
while i < n:
|
|
1258
|
-
|
|
1259
|
-
op = _op(c)
|
|
1316
|
+
op = bytecode[i]
|
|
1260
1317
|
i += 1
|
|
1261
1318
|
if op == CACHE:
|
|
1262
1319
|
continue
|
|
1263
|
-
if op
|
|
1320
|
+
if take_arg(op):
|
|
1264
1321
|
oparg = bytecode[i] | extended_arg
|
|
1265
1322
|
extended_arg = 0
|
|
1266
1323
|
if op == EXTENDED_ARG:
|
|
@@ -1277,9 +1334,40 @@ def _find_loads_without_stores_in_code(co, loads_without_stores):
|
|
|
1277
1334
|
stores.add(fullname)
|
|
1278
1335
|
continue
|
|
1279
1336
|
if op in [LOAD_ATTR, LOAD_METHOD]:
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1337
|
+
if sys.version_info >= (3,12):
|
|
1338
|
+
# from the docs:
|
|
1339
|
+
#
|
|
1340
|
+
# If the low bit of namei is not set, this replaces
|
|
1341
|
+
# STACK[-1] with getattr(STACK[-1], co_names[namei>>1]).
|
|
1342
|
+
#
|
|
1343
|
+
# If the low bit of namei is set, this will attempt to load
|
|
1344
|
+
# a method named co_names[namei>>1] from the STACK[-1]
|
|
1345
|
+
# object. STACK[-1] is popped. This bytecode distinguishes
|
|
1346
|
+
# two cases: if STACK[-1] has a method with the correct
|
|
1347
|
+
# name, the bytecode pushes the unbound method and
|
|
1348
|
+
# STACK[-1]. STACK[-1] will be used as the first argument
|
|
1349
|
+
# (self) by CALL when calling the unbound method. Otherwise,
|
|
1350
|
+
# NULL and the object returned by the attribute lookup are
|
|
1351
|
+
# pushed.
|
|
1352
|
+
#
|
|
1353
|
+
# Changed in version 3.12: If the low bit of namei is set,
|
|
1354
|
+
# then a NULL or self is pushed to the stack before the
|
|
1355
|
+
# attribute or unbound method respectively.
|
|
1356
|
+
#
|
|
1357
|
+
# Implication for Pyflyby
|
|
1358
|
+
#
|
|
1359
|
+
# In our case I think it means we are always looking at
|
|
1360
|
+
# oparg>>1 as the name of the names we need to load,
|
|
1361
|
+
# Though we don't keep track of the stack, and so we may get
|
|
1362
|
+
# wrong results ?
|
|
1363
|
+
#
|
|
1364
|
+
# In any case this seem to match what load_method was doing
|
|
1365
|
+
# before.
|
|
1366
|
+
pending.append(co.co_names[oparg>>1])
|
|
1367
|
+
else:
|
|
1368
|
+
# {LOAD_GLOBAL|LOAD_NAME} {LOAD_ATTR}* so far;
|
|
1369
|
+
# possibly more LOAD_ATTR/STORE_ATTR will follow
|
|
1370
|
+
pending.append(co.co_names[oparg])
|
|
1283
1371
|
continue
|
|
1284
1372
|
# {LOAD_GLOBAL|LOAD_NAME} {LOAD_ATTR}* (and no more
|
|
1285
1373
|
# LOAD_ATTR/STORE_ATTR)
|
|
@@ -1366,10 +1454,14 @@ def _find_loads_without_stores_in_code(co, loads_without_stores):
|
|
|
1366
1454
|
if isinstance(arg, types.CodeType):
|
|
1367
1455
|
_find_loads_without_stores_in_code(arg, loads_without_stores)
|
|
1368
1456
|
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1457
|
+
if sys.version_info >= (3,12):
|
|
1458
|
+
from dis import hasarg
|
|
1459
|
+
def take_arg(op):
|
|
1460
|
+
return op in hasarg
|
|
1461
|
+
else:
|
|
1462
|
+
def take_arg(op):
|
|
1463
|
+
from opcode import HAVE_ARGUMENT
|
|
1464
|
+
return op >= HAVE_ARGUMENT
|
|
1373
1465
|
|
|
1374
1466
|
def _find_earliest_backjump_label(bytecode):
|
|
1375
1467
|
"""
|
|
@@ -1411,19 +1503,20 @@ def _find_earliest_backjump_label(bytecode):
|
|
|
1411
1503
|
The earliest target of a backward jump, as an offset into the bytecode.
|
|
1412
1504
|
"""
|
|
1413
1505
|
# Code based on dis.findlabels().
|
|
1414
|
-
from opcode import
|
|
1506
|
+
from opcode import hasjrel, hasjabs
|
|
1415
1507
|
if not isinstance(bytecode, bytes):
|
|
1416
1508
|
raise TypeError
|
|
1417
1509
|
n = len(bytecode)
|
|
1418
1510
|
earliest_backjump_label = n
|
|
1419
1511
|
i = 0
|
|
1420
1512
|
while i < n:
|
|
1421
|
-
|
|
1422
|
-
op = _op(c)
|
|
1513
|
+
op = bytecode[i]
|
|
1423
1514
|
i += 1
|
|
1424
|
-
if op
|
|
1515
|
+
if not take_arg(op):
|
|
1425
1516
|
continue
|
|
1426
|
-
|
|
1517
|
+
if i+1 >= len(bytecode):
|
|
1518
|
+
break
|
|
1519
|
+
oparg = bytecode[i] + bytecode[i+1]*256
|
|
1427
1520
|
i += 2
|
|
1428
1521
|
label = None
|
|
1429
1522
|
if op in hasjrel:
|
|
@@ -1605,7 +1698,7 @@ def get_known_import(fullname, db=None):
|
|
|
1605
1698
|
return None
|
|
1606
1699
|
|
|
1607
1700
|
|
|
1608
|
-
_IMPORT_FAILED = set()
|
|
1701
|
+
_IMPORT_FAILED:Set[Any] = set()
|
|
1609
1702
|
"""
|
|
1610
1703
|
Set of imports we've already attempted and failed.
|
|
1611
1704
|
"""
|
|
@@ -1811,7 +1904,7 @@ def auto_import_symbol(fullname, namespaces, db=None, autoimported=None, post_im
|
|
|
1811
1904
|
return True
|
|
1812
1905
|
|
|
1813
1906
|
|
|
1814
|
-
def auto_import(arg, namespaces, db=None, autoimported=None, post_import_hook=None):
|
|
1907
|
+
def auto_import(arg, namespaces, db=None, autoimported=None, post_import_hook=None, *, extra_db=None):
|
|
1815
1908
|
"""
|
|
1816
1909
|
Parse ``arg`` for symbols that need to be imported and automatically import
|
|
1817
1910
|
them.
|
|
@@ -1865,6 +1958,8 @@ def auto_import(arg, namespaces, db=None, autoimported=None, post_import_hook=No
|
|
|
1865
1958
|
if autoimported is None:
|
|
1866
1959
|
autoimported = {}
|
|
1867
1960
|
db = ImportDB.interpret_arg(db, target_filename=filename)
|
|
1961
|
+
if extra_db:
|
|
1962
|
+
db = db|extra_db
|
|
1868
1963
|
ok = True
|
|
1869
1964
|
for fullname in fullnames:
|
|
1870
1965
|
ok &= auto_import_symbol(fullname, namespaces, db, autoimported, post_import_hook=post_import_hook)
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
from builtins import input
|
|
7
8
|
import optparse
|
|
8
9
|
import os
|
|
9
10
|
import signal
|
|
10
|
-
from builtins import input
|
|
11
11
|
import sys
|
|
12
12
|
from textwrap import dedent
|
|
13
13
|
import traceback
|
|
@@ -285,7 +285,8 @@ def parse_args(
|
|
|
285
285
|
def _default_on_error(filename):
|
|
286
286
|
raise SystemExit("bad filename %s" % (filename,))
|
|
287
287
|
|
|
288
|
-
|
|
288
|
+
|
|
289
|
+
def filename_args(args: List[str], on_error=_default_on_error):
|
|
289
290
|
"""
|
|
290
291
|
Return list of filenames given command-line arguments.
|
|
291
292
|
|
|
@@ -293,7 +294,9 @@ def filename_args(args, on_error=_default_on_error):
|
|
|
293
294
|
``list`` of `Filename`
|
|
294
295
|
"""
|
|
295
296
|
if args:
|
|
296
|
-
|
|
297
|
+
for a in args:
|
|
298
|
+
assert isinstance(a, str)
|
|
299
|
+
return expand_py_files_from_args([Filename(f) for f in args], on_error)
|
|
297
300
|
elif not os.isatty(0):
|
|
298
301
|
return [Filename.STDIN]
|
|
299
302
|
else:
|
|
@@ -415,8 +418,8 @@ def process_actions(filenames:List[str], actions, modify_function,
|
|
|
415
418
|
continue
|
|
416
419
|
if errors:
|
|
417
420
|
msg = "\n%s: encountered the following problems:\n" % (sys.argv[0],)
|
|
418
|
-
for
|
|
419
|
-
lines =
|
|
421
|
+
for er in errors:
|
|
422
|
+
lines = er.splitlines()
|
|
420
423
|
msg += " " + lines[0] + '\n'.join(
|
|
421
424
|
(" %s"%line for line in lines[1:]))
|
|
422
425
|
raise SystemExit(msg)
|
|
@@ -9,6 +9,8 @@ from pyflyby._imports2s import (SourceToSourceFileImportsTransformation,
|
|
|
9
9
|
from pyflyby._importstmt import Import
|
|
10
10
|
from pyflyby._log import logger
|
|
11
11
|
|
|
12
|
+
from typing import Dict, Any
|
|
13
|
+
|
|
12
14
|
# These are comm targets that the frontend (lab/notebook) is expected to
|
|
13
15
|
# open. At this point, we handle only missing imports and
|
|
14
16
|
# formatting imports
|
|
@@ -23,7 +25,7 @@ PYFLYBY_END_MSG = "# END AUTO-GENERATED BLOCK\n"
|
|
|
23
25
|
pyflyby_comm_targets= [MISSING_IMPORTS, FORMATTING_IMPORTS, TIDY_IMPORTS]
|
|
24
26
|
|
|
25
27
|
# A map of the comms opened with a given target name.
|
|
26
|
-
comms = {}
|
|
28
|
+
comms:Dict[str, Any] = {}
|
|
27
29
|
|
|
28
30
|
# TODO: Document the expected contract for the different
|
|
29
31
|
# custom comm messages
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Virtual module to create dynamic import at runtime.
|
|
3
|
+
|
|
4
|
+
It is sometime desirable to have auto import which are define only during
|
|
5
|
+
a session and never exist on a on-disk file.
|
|
6
|
+
|
|
7
|
+
This is injects a Dict module loader as well as a dictionary registry of in
|
|
8
|
+
memory module.
|
|
9
|
+
|
|
10
|
+
This is mostly use in IPython for lazy variable initialisation without having
|
|
11
|
+
to use proxy objects.
|
|
12
|
+
|
|
13
|
+
To use, put the following in your IPython startup files
|
|
14
|
+
(``~/.ipython/profile_default/startup/autoimp.py`), or in your IPython
|
|
15
|
+
configuration file:
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
.. code:: python
|
|
19
|
+
|
|
20
|
+
from pyflyby._dynimp import add_import
|
|
21
|
+
|
|
22
|
+
add_import("foo", "foo = 1")
|
|
23
|
+
|
|
24
|
+
add_import(
|
|
25
|
+
"df, data",
|
|
26
|
+
'''
|
|
27
|
+
import pandas as pd
|
|
28
|
+
data = [1,2,3]
|
|
29
|
+
df = pd.DataFrame(data)
|
|
30
|
+
''',
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
Now at the IPython prompt, if the pyflyby extension is loaded (either because
|
|
34
|
+
you started using the ``py`` cli, or some configuration options like ``ipython
|
|
35
|
+
--TerminalIPythonApp.extra_extensions=pyflyby``. When trying to use an undefined
|
|
36
|
+
variable like ``foo``, ``df`` or ``data``, the corresponding module will be
|
|
37
|
+
executed and the relevant variable imported.
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
"""
|
|
41
|
+
import importlib.abc
|
|
42
|
+
import importlib.util
|
|
43
|
+
import sys
|
|
44
|
+
|
|
45
|
+
from textwrap import dedent
|
|
46
|
+
from typing import FrozenSet
|
|
47
|
+
|
|
48
|
+
from pyflyby._importclns import ImportSet, Import
|
|
49
|
+
|
|
50
|
+
module_dict = {}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def add_import(names: str, code: str, *, strict: bool = True):
|
|
54
|
+
"""
|
|
55
|
+
Add a runtime generated import module
|
|
56
|
+
|
|
57
|
+
Parameters
|
|
58
|
+
----------
|
|
59
|
+
names: str
|
|
60
|
+
name, or comma separated list variable names that should be created by
|
|
61
|
+
executing and importing `code`.
|
|
62
|
+
code: str
|
|
63
|
+
potentially multiline string that will be turned into a module,
|
|
64
|
+
executed and from which variables listed in names can be imported.
|
|
65
|
+
strict: bool
|
|
66
|
+
Raise in case of problem loading IPython of if pyflyby extension not installed.
|
|
67
|
+
otherwise just ignore error
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
Examples
|
|
72
|
+
--------
|
|
73
|
+
|
|
74
|
+
>>> add_import('pd, df', '''
|
|
75
|
+
... import pandas a pd
|
|
76
|
+
...
|
|
77
|
+
... df = pd.DataFrame([[1,2], [3,4]])
|
|
78
|
+
... ''', strict=False) # don't fail doctest
|
|
79
|
+
|
|
80
|
+
"""
|
|
81
|
+
try:
|
|
82
|
+
ip = _raise_if_problem()
|
|
83
|
+
except Exception:
|
|
84
|
+
if strict:
|
|
85
|
+
raise
|
|
86
|
+
else:
|
|
87
|
+
return
|
|
88
|
+
return _add_import(ip, names, code)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _raise_if_problem():
|
|
92
|
+
try:
|
|
93
|
+
import IPython
|
|
94
|
+
except ModuleNotFoundError as e:
|
|
95
|
+
raise ImportError("Dynamic autoimport requires IPython to be installed") from e
|
|
96
|
+
|
|
97
|
+
ip = IPython.get_ipython()
|
|
98
|
+
if ip is None:
|
|
99
|
+
raise ImportError("Dynamic autoimport only work from within IPython")
|
|
100
|
+
|
|
101
|
+
if not hasattr(ip, "_auto_importer"):
|
|
102
|
+
raise ValueError(
|
|
103
|
+
"IPython needs to be loaded with pyflyby extension for lazy variable to work"
|
|
104
|
+
)
|
|
105
|
+
return ip
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _add_import(ip, names: str, code: str) -> None:
|
|
109
|
+
"""
|
|
110
|
+
private version of add_import
|
|
111
|
+
"""
|
|
112
|
+
assert ip is not None
|
|
113
|
+
mang = "pyflyby_autoimport_" + names.replace(",", "_").replace(" ", "_")
|
|
114
|
+
a: FrozenSet[Import] = ImportSet(f"from {mang} import {names}")._importset
|
|
115
|
+
b: FrozenSet[Import] = ip._auto_importer.db.known_imports._importset
|
|
116
|
+
s_import: FrozenSet[Import] = a | b
|
|
117
|
+
|
|
118
|
+
ip._auto_importer.db.known_imports = ImportSet._from_imports(list(s_import))
|
|
119
|
+
module_dict[mang] = dedent(code)
|
|
120
|
+
|
|
121
|
+
class DictLoader(importlib.abc.Loader):
|
|
122
|
+
"""
|
|
123
|
+
A dict based loader for in-memory module definition.
|
|
124
|
+
"""
|
|
125
|
+
def __init__(self, module_name, module_code):
|
|
126
|
+
self.module_name = module_name
|
|
127
|
+
self.module_code = module_code
|
|
128
|
+
|
|
129
|
+
def create_module(self, spec):
|
|
130
|
+
return None # Use default module creation semantics
|
|
131
|
+
|
|
132
|
+
def exec_module(self, module):
|
|
133
|
+
"""
|
|
134
|
+
we exec module code directly in memory
|
|
135
|
+
"""
|
|
136
|
+
exec(self.module_code, module.__dict__)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class DictFinder(importlib.abc.MetaPathFinder):
|
|
140
|
+
"""
|
|
141
|
+
A meta path finder for abode DictLoader
|
|
142
|
+
"""
|
|
143
|
+
def find_spec(self, fullname, path, target=None):
|
|
144
|
+
if fullname in module_dict:
|
|
145
|
+
module_code = module_dict[fullname]
|
|
146
|
+
loader = DictLoader(fullname, module_code)
|
|
147
|
+
return importlib.util.spec_from_loader(fullname, loader)
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def inject():
|
|
152
|
+
sys.meta_path.insert(0, DictFinder())
|