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.

Files changed (155) hide show
  1. {pyflyby-1.9.3/lib/python/pyflyby.egg-info → pyflyby-1.9.5}/PKG-INFO +37 -3
  2. {pyflyby-1.9.3 → pyflyby-1.9.5}/README.rst +31 -1
  3. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/autopython +1 -1
  4. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/create-imports +1 -1
  5. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/__init__.py +4 -1
  6. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_autoimp.py +130 -35
  7. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_cmdline.py +8 -5
  8. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_comms.py +3 -1
  9. pyflyby-1.9.5/bin/pyflyby/_dynimp.py +152 -0
  10. {pyflyby-1.9.3/lib/python → pyflyby-1.9.5/bin}/pyflyby/_file.py +41 -22
  11. {pyflyby-1.9.3/lib/python → pyflyby-1.9.5/bin}/pyflyby/_flags.py +1 -1
  12. pyflyby-1.9.5/bin/pyflyby/_import_sorting.py +165 -0
  13. {pyflyby-1.9.3/lib/python → pyflyby-1.9.5/bin}/pyflyby/_importclns.py +51 -21
  14. {pyflyby-1.9.3/lib/python → pyflyby-1.9.5/bin}/pyflyby/_importdb.py +134 -44
  15. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_imports2s.py +54 -84
  16. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_importstmt.py +52 -11
  17. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_interactive.py +24 -147
  18. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_livepatch.py +4 -1
  19. {pyflyby-1.9.3/lib/python → pyflyby-1.9.5/bin}/pyflyby/_modules.py +20 -5
  20. {pyflyby-1.9.3/lib/python → pyflyby-1.9.5/bin}/pyflyby/_parse.py +267 -242
  21. {pyflyby-1.9.3/lib/python → pyflyby-1.9.5/bin}/pyflyby/_py.py +12 -22
  22. {pyflyby-1.9.3/lib/python → pyflyby-1.9.5/bin}/pyflyby/_version.py +1 -2
  23. {pyflyby-1.9.3/lib/python → pyflyby-1.9.5/bin}/pyflyby/autoimport.py +1 -1
  24. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby-diff +1 -1
  25. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/tidy-imports +37 -14
  26. {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/__init__.py +4 -1
  27. {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_autoimp.py +130 -35
  28. {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_cmdline.py +8 -5
  29. {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_comms.py +3 -1
  30. pyflyby-1.9.5/lib/python/pyflyby/_dynimp.py +152 -0
  31. {pyflyby-1.9.3/bin → pyflyby-1.9.5/lib/python}/pyflyby/_file.py +41 -22
  32. {pyflyby-1.9.3/bin → pyflyby-1.9.5/lib/python}/pyflyby/_flags.py +1 -1
  33. pyflyby-1.9.5/lib/python/pyflyby/_import_sorting.py +165 -0
  34. {pyflyby-1.9.3/bin → pyflyby-1.9.5/lib/python}/pyflyby/_importclns.py +51 -21
  35. {pyflyby-1.9.3/bin → pyflyby-1.9.5/lib/python}/pyflyby/_importdb.py +134 -44
  36. {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_imports2s.py +54 -84
  37. {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_importstmt.py +52 -11
  38. {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_interactive.py +24 -147
  39. {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_livepatch.py +4 -1
  40. {pyflyby-1.9.3/bin → pyflyby-1.9.5/lib/python}/pyflyby/_modules.py +20 -5
  41. {pyflyby-1.9.3/bin → pyflyby-1.9.5/lib/python}/pyflyby/_parse.py +267 -242
  42. {pyflyby-1.9.3/bin → pyflyby-1.9.5/lib/python}/pyflyby/_py.py +12 -22
  43. {pyflyby-1.9.3/bin → pyflyby-1.9.5/lib/python}/pyflyby/_version.py +1 -2
  44. {pyflyby-1.9.3/bin → pyflyby-1.9.5/lib/python}/pyflyby/autoimport.py +1 -1
  45. {pyflyby-1.9.3 → pyflyby-1.9.5/lib/python/pyflyby.egg-info}/PKG-INFO +37 -3
  46. {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby.egg-info/SOURCES.txt +10 -0
  47. pyflyby-1.9.5/lib/python/pyflyby.egg-info/requires.txt +6 -0
  48. {pyflyby-1.9.3 → pyflyby-1.9.5}/libexec/pyflyby/colordiff +1 -1
  49. pyflyby-1.9.5/pyproject.toml +26 -0
  50. {pyflyby-1.9.3 → pyflyby-1.9.5}/setup.py +7 -2
  51. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_autoimp.py +110 -6
  52. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_cmdline.py +46 -14
  53. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_importclns.py +6 -0
  54. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_imports2s.py +3 -0
  55. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_interactive.py +29 -17
  56. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_jupyterlab_pyflyby.py +2 -1
  57. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_livepatch.py +1 -1
  58. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_parse.py +128 -45
  59. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_py.py +228 -226
  60. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/tests_sorts.py +65 -12
  61. pyflyby-1.9.3/lib/python/pyflyby.egg-info/requires.txt +0 -4
  62. {pyflyby-1.9.3 → pyflyby-1.9.5}/.pyflyby +0 -0
  63. {pyflyby-1.9.3 → pyflyby-1.9.5}/LICENSE.txt +0 -0
  64. {pyflyby-1.9.3 → pyflyby-1.9.5}/MANIFEST.in +0 -0
  65. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/autoipython +0 -0
  66. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/collect-exports +0 -0
  67. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/collect-imports +0 -0
  68. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/find-import +0 -0
  69. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/list-bad-xrefs +0 -0
  70. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/prune-broken-imports +0 -0
  71. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/py +0 -0
  72. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/__main__.py +0 -0
  73. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_dbg.py +0 -0
  74. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_docxref.py +0 -0
  75. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_format.py +0 -0
  76. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_idents.py +0 -0
  77. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_log.py +0 -0
  78. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/_util.py +0 -0
  79. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/pyflyby/importdb.py +0 -0
  80. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/reformat-imports +0 -0
  81. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/replace-star-imports +0 -0
  82. {pyflyby-1.9.3 → pyflyby-1.9.5}/bin/transform-imports +0 -0
  83. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/LICENSE.txt +0 -0
  84. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/Makefile +0 -0
  85. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/TODO.txt +0 -0
  86. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/__init__.py +0 -0
  87. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/api.rst +0 -0
  88. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/autoimp.rst +0 -0
  89. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/cmdline.rst +0 -0
  90. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/comms.rst +0 -0
  91. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/dbg.rst +0 -0
  92. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/file.rst +0 -0
  93. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/flags.rst +0 -0
  94. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/format.rst +0 -0
  95. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/idents.rst +0 -0
  96. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/importclns.rst +0 -0
  97. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/importdb.rst +0 -0
  98. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/imports2s.rst +0 -0
  99. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/importstmt.rst +0 -0
  100. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/interactive.rst +0 -0
  101. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/livepatch.rst +0 -0
  102. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/log.rst +0 -0
  103. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/modules.rst +0 -0
  104. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/parse.rst +0 -0
  105. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/py.rst +0 -0
  106. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/api/util.rst +0 -0
  107. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/autoipython.rst +0 -0
  108. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/cli.rst +0 -0
  109. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/collect_exports.rst +0 -0
  110. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/collect_imports.rst +0 -0
  111. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/find_import.rst +0 -0
  112. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/prune_broken_imports.rst +0 -0
  113. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/py.rst +0 -0
  114. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/pyflyby_diff.rst +0 -0
  115. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/reformat_imports.rst +0 -0
  116. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/replace_star_imports.rst +0 -0
  117. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/tidy_imports.rst +0 -0
  118. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/cli/transform_imports.rst +0 -0
  119. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/conf.py +0 -0
  120. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/index.rst +0 -0
  121. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/make.bat +0 -0
  122. {pyflyby-1.9.3 → pyflyby-1.9.5}/doc/testing.txt +0 -0
  123. {pyflyby-1.9.3 → pyflyby-1.9.5}/etc/pyflyby/canonical.py +0 -0
  124. {pyflyby-1.9.3 → pyflyby-1.9.5}/etc/pyflyby/common.py +0 -0
  125. {pyflyby-1.9.3 → pyflyby-1.9.5}/etc/pyflyby/forget.py +0 -0
  126. {pyflyby-1.9.3 → pyflyby-1.9.5}/etc/pyflyby/mandatory.py +0 -0
  127. {pyflyby-1.9.3 → pyflyby-1.9.5}/etc/pyflyby/numpy.py +0 -0
  128. {pyflyby-1.9.3 → pyflyby-1.9.5}/etc/pyflyby/std.py +0 -0
  129. {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/emacs/pyflyby.el +0 -0
  130. {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/__main__.py +0 -0
  131. {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_dbg.py +0 -0
  132. {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_docxref.py +0 -0
  133. {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_format.py +0 -0
  134. {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_idents.py +0 -0
  135. {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_log.py +0 -0
  136. {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/_util.py +0 -0
  137. {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby/importdb.py +0 -0
  138. {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby.egg-info/dependency_links.txt +0 -0
  139. {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby.egg-info/entry_points.txt +0 -0
  140. {pyflyby-1.9.3 → pyflyby-1.9.5}/lib/python/pyflyby.egg-info/top_level.txt +0 -0
  141. {pyflyby-1.9.3 → pyflyby-1.9.5}/libexec/pyflyby/diff-colorize +0 -0
  142. {pyflyby-1.9.3 → pyflyby-1.9.5}/setup.cfg +0 -0
  143. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/__init__.py +0 -0
  144. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_0testconfig.py +0 -0
  145. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_docxref.py +0 -0
  146. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_file.py +0 -0
  147. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_flags.py +0 -0
  148. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_format.py +0 -0
  149. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_idents.py +0 -0
  150. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_importdb.py +0 -0
  151. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_importstmt.py +0 -0
  152. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_modules.py +0 -0
  153. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/test_util.py +0 -0
  154. {pyflyby-1.9.3 → pyflyby-1.9.5}/tests/xrefs.py +0 -0
  155. {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
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.6, <4
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.7+.
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.7+.
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
  ===========================================
@@ -1,4 +1,4 @@
1
- #!/bin/bash -e
1
+ #!/usr/bin/env -S bash -e
2
2
 
3
3
  # pyflyby/autopython
4
4
 
@@ -1,4 +1,4 @@
1
- #!/bin/bash -e
1
+ #!/usr/bin/env -S bash -e
2
2
 
3
3
  # pyflyby/create-imports
4
4
 
@@ -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, infer_compile_mode, _is_ast_str
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
- if sys.version_info > (3, 11):
38
- LOAD_SHIFT = 1
39
+
40
+ if sys.version_info >= (3, 10):
41
+ from types import NoneType, EllipsisType
39
42
  else:
40
- LOAD_SHIFT = 0
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
- codeblock = PythonBlock(codeblock)
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
- visitor = getattr(self, method, self.generic_visit)
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
- self._visit_Load_defered_global(e.s)
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
- codeblock = PythonBlock(codeblock)
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 HAVE_ARGUMENT, EXTENDED_ARG, opmap
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
- c = bytecode[i]
1259
- op = _op(c)
1316
+ op = bytecode[i]
1260
1317
  i += 1
1261
1318
  if op == CACHE:
1262
1319
  continue
1263
- if op >= HAVE_ARGUMENT:
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
- # {LOAD_GLOBAL|LOAD_NAME} {LOAD_ATTR}* so far;
1281
- # possibly more LOAD_ATTR/STORE_ATTR will follow
1282
- pending.append(co.co_names[oparg])
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
- def _op(c):
1371
- return c
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 HAVE_ARGUMENT, hasjrel, hasjabs
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
- c = bytecode[i]
1422
- op = _op(c)
1513
+ op = bytecode[i]
1423
1514
  i += 1
1424
- if op < HAVE_ARGUMENT:
1515
+ if not take_arg(op):
1425
1516
  continue
1426
- oparg = _op(bytecode[i]) + _op(bytecode[i+1])*256
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
- def filename_args(args, on_error=_default_on_error):
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
- return expand_py_files_from_args(args, on_error)
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 e in errors:
419
- lines = e.splitlines()
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())