pyflyby 1.9.7__tar.gz → 1.9.9__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 (158) hide show
  1. {pyflyby-1.9.7/lib/python/pyflyby.egg-info → pyflyby-1.9.9}/PKG-INFO +11 -11
  2. {pyflyby-1.9.7 → pyflyby-1.9.9}/README.rst +9 -9
  3. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/_autoimp.py +98 -38
  4. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/_dbg.py +8 -0
  5. {pyflyby-1.9.7/lib/python → pyflyby-1.9.9/bin}/pyflyby/_dynimp.py +3 -2
  6. {pyflyby-1.9.7/lib/python → pyflyby-1.9.9/bin}/pyflyby/_importdb.py +8 -4
  7. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/_imports2s.py +10 -9
  8. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/_importstmt.py +1 -1
  9. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/_interactive.py +20 -202
  10. {pyflyby-1.9.7/lib/python → pyflyby-1.9.9/bin}/pyflyby/_parse.py +27 -94
  11. {pyflyby-1.9.7/lib/python → pyflyby-1.9.9/bin}/pyflyby/_py.py +18 -18
  12. {pyflyby-1.9.7/lib/python → pyflyby-1.9.9/bin}/pyflyby/_saveframe.py +1 -1
  13. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/_util.py +1 -2
  14. {pyflyby-1.9.7/lib/python → pyflyby-1.9.9/bin}/pyflyby/_version.py +1 -1
  15. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/saveframe +2 -2
  16. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/_autoimp.py +98 -38
  17. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/_dbg.py +8 -0
  18. {pyflyby-1.9.7/bin → pyflyby-1.9.9/lib/python}/pyflyby/_dynimp.py +3 -2
  19. {pyflyby-1.9.7/bin → pyflyby-1.9.9/lib/python}/pyflyby/_importdb.py +8 -4
  20. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/_imports2s.py +10 -9
  21. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/_importstmt.py +1 -1
  22. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/_interactive.py +20 -202
  23. {pyflyby-1.9.7/bin → pyflyby-1.9.9/lib/python}/pyflyby/_parse.py +27 -94
  24. {pyflyby-1.9.7/bin → pyflyby-1.9.9/lib/python}/pyflyby/_py.py +18 -18
  25. {pyflyby-1.9.7/bin → pyflyby-1.9.9/lib/python}/pyflyby/_saveframe.py +1 -1
  26. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/_util.py +1 -2
  27. {pyflyby-1.9.7/bin → pyflyby-1.9.9/lib/python}/pyflyby/_version.py +1 -1
  28. {pyflyby-1.9.7 → pyflyby-1.9.9/lib/python/pyflyby.egg-info}/PKG-INFO +11 -11
  29. {pyflyby-1.9.7 → pyflyby-1.9.9}/setup.py +1 -1
  30. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/test_autoimp.py +11 -1
  31. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/test_modules.py +3 -1
  32. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/test_parse.py +36 -0
  33. {pyflyby-1.9.7 → pyflyby-1.9.9}/.pyflyby +0 -0
  34. {pyflyby-1.9.7 → pyflyby-1.9.9}/LICENSE.txt +0 -0
  35. {pyflyby-1.9.7 → pyflyby-1.9.9}/MANIFEST.in +0 -0
  36. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/autoipython +0 -0
  37. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/autopython +0 -0
  38. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/collect-exports +0 -0
  39. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/collect-imports +0 -0
  40. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/create-imports +0 -0
  41. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/find-import +0 -0
  42. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/list-bad-xrefs +0 -0
  43. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/prune-broken-imports +0 -0
  44. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/py +0 -0
  45. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/__init__.py +0 -0
  46. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/__main__.py +0 -0
  47. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/_cmdline.py +0 -0
  48. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/_comms.py +0 -0
  49. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/_docxref.py +0 -0
  50. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/_file.py +0 -0
  51. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/_flags.py +0 -0
  52. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/_format.py +0 -0
  53. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/_idents.py +0 -0
  54. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/_import_sorting.py +0 -0
  55. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/_importclns.py +0 -0
  56. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/_livepatch.py +0 -0
  57. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/_log.py +0 -0
  58. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/_modules.py +0 -0
  59. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/autoimport.py +0 -0
  60. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby/importdb.py +0 -0
  61. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/pyflyby-diff +0 -0
  62. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/reformat-imports +0 -0
  63. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/replace-star-imports +0 -0
  64. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/tidy-imports +0 -0
  65. {pyflyby-1.9.7 → pyflyby-1.9.9}/bin/transform-imports +0 -0
  66. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/LICENSE.txt +0 -0
  67. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/Makefile +0 -0
  68. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/TODO.txt +0 -0
  69. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/__init__.py +0 -0
  70. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/api/api.rst +0 -0
  71. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/api/autoimp.rst +0 -0
  72. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/api/cmdline.rst +0 -0
  73. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/api/comms.rst +0 -0
  74. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/api/dbg.rst +0 -0
  75. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/api/file.rst +0 -0
  76. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/api/flags.rst +0 -0
  77. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/api/format.rst +0 -0
  78. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/api/idents.rst +0 -0
  79. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/api/importclns.rst +0 -0
  80. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/api/importdb.rst +0 -0
  81. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/api/imports2s.rst +0 -0
  82. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/api/importstmt.rst +0 -0
  83. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/api/interactive.rst +0 -0
  84. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/api/livepatch.rst +0 -0
  85. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/api/log.rst +0 -0
  86. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/api/modules.rst +0 -0
  87. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/api/parse.rst +0 -0
  88. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/api/py.rst +0 -0
  89. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/api/util.rst +0 -0
  90. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/cli/autoipython.rst +0 -0
  91. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/cli/cli.rst +0 -0
  92. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/cli/collect_exports.rst +0 -0
  93. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/cli/collect_imports.rst +0 -0
  94. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/cli/find_import.rst +0 -0
  95. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/cli/prune_broken_imports.rst +0 -0
  96. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/cli/py.rst +0 -0
  97. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/cli/pyflyby_diff.rst +0 -0
  98. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/cli/reformat_imports.rst +0 -0
  99. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/cli/replace_star_imports.rst +0 -0
  100. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/cli/tidy_imports.rst +0 -0
  101. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/cli/transform_imports.rst +0 -0
  102. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/conf.py +0 -0
  103. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/index.rst +0 -0
  104. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/make.bat +0 -0
  105. {pyflyby-1.9.7 → pyflyby-1.9.9}/doc/testing.txt +0 -0
  106. {pyflyby-1.9.7 → pyflyby-1.9.9}/etc/pyflyby/canonical.py +0 -0
  107. {pyflyby-1.9.7 → pyflyby-1.9.9}/etc/pyflyby/common.py +0 -0
  108. {pyflyby-1.9.7 → pyflyby-1.9.9}/etc/pyflyby/forget.py +0 -0
  109. {pyflyby-1.9.7 → pyflyby-1.9.9}/etc/pyflyby/mandatory.py +0 -0
  110. {pyflyby-1.9.7 → pyflyby-1.9.9}/etc/pyflyby/numpy.py +0 -0
  111. {pyflyby-1.9.7 → pyflyby-1.9.9}/etc/pyflyby/std.py +0 -0
  112. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/emacs/pyflyby.el +0 -0
  113. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/__init__.py +0 -0
  114. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/__main__.py +0 -0
  115. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/_cmdline.py +0 -0
  116. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/_comms.py +0 -0
  117. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/_docxref.py +0 -0
  118. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/_file.py +0 -0
  119. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/_flags.py +0 -0
  120. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/_format.py +0 -0
  121. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/_idents.py +0 -0
  122. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/_import_sorting.py +0 -0
  123. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/_importclns.py +0 -0
  124. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/_livepatch.py +0 -0
  125. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/_log.py +0 -0
  126. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/_modules.py +0 -0
  127. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/autoimport.py +0 -0
  128. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby/importdb.py +0 -0
  129. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby.egg-info/SOURCES.txt +0 -0
  130. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby.egg-info/dependency_links.txt +0 -0
  131. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby.egg-info/entry_points.txt +0 -0
  132. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby.egg-info/requires.txt +0 -0
  133. {pyflyby-1.9.7 → pyflyby-1.9.9}/lib/python/pyflyby.egg-info/top_level.txt +0 -0
  134. {pyflyby-1.9.7 → pyflyby-1.9.9}/libexec/pyflyby/colordiff +0 -0
  135. {pyflyby-1.9.7 → pyflyby-1.9.9}/libexec/pyflyby/diff-colorize +0 -0
  136. {pyflyby-1.9.7 → pyflyby-1.9.9}/pyproject.toml +0 -0
  137. {pyflyby-1.9.7 → pyflyby-1.9.9}/setup.cfg +0 -0
  138. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/__init__.py +0 -0
  139. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/test_0testconfig.py +0 -0
  140. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/test_cmdline.py +0 -0
  141. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/test_docxref.py +0 -0
  142. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/test_file.py +0 -0
  143. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/test_flags.py +0 -0
  144. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/test_format.py +0 -0
  145. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/test_idents.py +0 -0
  146. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/test_importclns.py +0 -0
  147. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/test_importdb.py +0 -0
  148. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/test_imports2s.py +0 -0
  149. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/test_importstmt.py +0 -0
  150. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/test_interactive.py +0 -0
  151. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/test_jupyterlab_pyflyby.py +0 -0
  152. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/test_livepatch.py +0 -0
  153. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/test_py.py +0 -0
  154. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/test_saveframe.py +0 -0
  155. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/test_util.py +0 -0
  156. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/tests_sorts.py +0 -0
  157. {pyflyby-1.9.7 → pyflyby-1.9.9}/tests/xrefs.py +0 -0
  158. {pyflyby-1.9.7 → pyflyby-1.9.9}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyflyby
3
- Version: 1.9.7
3
+ Version: 1.9.9
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,7 +16,7 @@ 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.8, <4
19
+ Requires-Python: >3.9, <4
20
20
  License-File: LICENSE.txt
21
21
 
22
22
  #########
@@ -113,7 +113,7 @@ configuration file:
113
113
 
114
114
  .. code:: python
115
115
 
116
-
116
+
117
117
  from pyflyby import add_import
118
118
 
119
119
  add_import("foo", "foo = 1")
@@ -484,29 +484,29 @@ Emacs support
484
484
  saveframe: A utility for debugging / reproducing an issue
485
485
  =========================================================
486
486
 
487
- PyFlyBy provides a utility named **saveframe** which can be used to save
487
+ PyFlyBy provides a utility named **saveframe** which can be used to save
488
488
  information for debugging / reproducing an issue.
489
489
 
490
- **Usage**: If you have a piece of code or a script that is failing due an issue
491
- originating from upstream code, and you cannot share your private code as a reproducer,
492
- use this utility to save relevant information to a file. Share the generated file with
490
+ **Usage**: If you have a piece of code or a script that is failing due an issue
491
+ originating from upstream code, and you cannot share your private code as a reproducer,
492
+ use this utility to save relevant information to a file. Share the generated file with
493
493
  the upstream team, enabling them to reproduce and diagnose the issue independently.
494
494
 
495
- **Information saved in the file**: This utility captures and saves *error stack frames*
496
- to a file. It includes the values of local variables from each stack frame, as well
495
+ **Information saved in the file**: This utility captures and saves *error stack frames*
496
+ to a file. It includes the values of local variables from each stack frame, as well
497
497
  as metadata about each frame and the exception raised by your code.
498
498
 
499
499
  This utility comes with 2 interfaces:
500
500
 
501
501
  1. **A function**: For interactive usages such as IPython, Jupyter Notebook, or a
502
- debugger (pdb/ipdb), use **pyflyby.saveframe** function. To know how to use this
502
+ debugger (pdb/ipdb), use **pyflyby.saveframe** function. To know how to use this
503
503
  function, checkout it's documentation:
504
504
 
505
505
  .. code::
506
506
 
507
507
  In [1]: saveframe?
508
508
 
509
- 2. **A script**: For cli usages (like a failing script), use **pyflyby/bin/saveframe**
509
+ 2. **A script**: For cli usages (like a failing script), use **pyflyby/bin/saveframe**
510
510
  script. To know how to use this script, checkout its documentation:
511
511
 
512
512
  .. code::
@@ -92,7 +92,7 @@ configuration file:
92
92
 
93
93
  .. code:: python
94
94
 
95
-
95
+
96
96
  from pyflyby import add_import
97
97
 
98
98
  add_import("foo", "foo = 1")
@@ -463,29 +463,29 @@ Emacs support
463
463
  saveframe: A utility for debugging / reproducing an issue
464
464
  =========================================================
465
465
 
466
- PyFlyBy provides a utility named **saveframe** which can be used to save
466
+ PyFlyBy provides a utility named **saveframe** which can be used to save
467
467
  information for debugging / reproducing an issue.
468
468
 
469
- **Usage**: If you have a piece of code or a script that is failing due an issue
470
- originating from upstream code, and you cannot share your private code as a reproducer,
471
- use this utility to save relevant information to a file. Share the generated file with
469
+ **Usage**: If you have a piece of code or a script that is failing due an issue
470
+ originating from upstream code, and you cannot share your private code as a reproducer,
471
+ use this utility to save relevant information to a file. Share the generated file with
472
472
  the upstream team, enabling them to reproduce and diagnose the issue independently.
473
473
 
474
- **Information saved in the file**: This utility captures and saves *error stack frames*
475
- to a file. It includes the values of local variables from each stack frame, as well
474
+ **Information saved in the file**: This utility captures and saves *error stack frames*
475
+ to a file. It includes the values of local variables from each stack frame, as well
476
476
  as metadata about each frame and the exception raised by your code.
477
477
 
478
478
  This utility comes with 2 interfaces:
479
479
 
480
480
  1. **A function**: For interactive usages such as IPython, Jupyter Notebook, or a
481
- debugger (pdb/ipdb), use **pyflyby.saveframe** function. To know how to use this
481
+ debugger (pdb/ipdb), use **pyflyby.saveframe** function. To know how to use this
482
482
  function, checkout it's documentation:
483
483
 
484
484
  .. code::
485
485
 
486
486
  In [1]: saveframe?
487
487
 
488
- 2. **A script**: For cli usages (like a failing script), use **pyflyby/bin/saveframe**
488
+ 2. **A script**: For cli usages (like a failing script), use **pyflyby/bin/saveframe**
489
489
  script. To know how to use this script, checkout its documentation:
490
490
 
491
491
  .. code::
@@ -1,9 +1,11 @@
1
1
  # pyflyby/_autoimp.py.
2
- # Copyright (C) 2011, 2012, 2013, 2014, 2015, 2018, 2019 Karl Chen.
2
+ # Copyright (C) 2011, 2012, 2013, 2014, 2015, 2018, 2019, 2024 Karl Chen.
3
3
  # License: MIT http://opensource.org/licenses/MIT
4
4
 
5
5
 
6
6
 
7
+ from __future__ import annotations, print_function
8
+
7
9
  import ast
8
10
  import builtins
9
11
  from collections.abc import Sequence
@@ -18,13 +20,14 @@ from pyflyby._importdb import ImportDB
18
20
  from pyflyby._importstmt import Import
19
21
  from pyflyby._log import logger
20
22
  from pyflyby._modules import ModuleHandle
21
- from pyflyby._parse import (PythonBlock, _is_ast_str,
22
- infer_compile_mode, MatchAs)
23
+ from pyflyby._parse import (MatchAs, PythonBlock, _is_ast_str,
24
+ infer_compile_mode)
23
25
 
24
26
  from six import reraise
25
27
  import sys
26
28
  import types
27
- from typing import Any, Set
29
+ from typing import (Any, Dict, List, Optional, Set, Tuple,
30
+ Union)
28
31
 
29
32
  if sys.version_info >= (3, 12):
30
33
  ATTRIBUTE_NAME = "value"
@@ -130,9 +133,13 @@ class ScopeStack(Sequence):
130
133
  def __len__(self):
131
134
  return len(self._tup)
132
135
 
133
- def with_new_scope(
134
- self, include_class_scopes=False, new_class_scope=False, unhide_classdef=False
135
- ):
136
+ def _with_new_scope(
137
+ self,
138
+ *,
139
+ include_class_scopes: bool,
140
+ new_class_scope: bool,
141
+ unhide_classdef: bool,
142
+ ) -> ScopeStack:
136
143
  """
137
144
  Return a new ``ScopeStack`` with an additional empty scope.
138
145
 
@@ -148,8 +155,8 @@ class ScopeStack(Sequence):
148
155
  if include_class_scopes:
149
156
  scopes = tuple(self)
150
157
  else:
151
- scopes = tuple(s for s in self
152
- if not isinstance(s, _ClassScope))
158
+ scopes = tuple(s for s in self if not isinstance(s, _ClassScope))
159
+ new_scope: Union[_ClassScope, Dict[str, Any]]
153
160
  if new_class_scope:
154
161
  new_scope = _ClassScope()
155
162
  else:
@@ -199,7 +206,7 @@ class ScopeStack(Sequence):
199
206
  return (d, self[-1])
200
207
 
201
208
 
202
- def has_star_import(self):
209
+ def has_star_import(self) -> bool:
203
210
  """
204
211
  Return whether there are any star-imports in this ScopeStack.
205
212
  Only relevant in AST-based static analysis mode.
@@ -341,19 +348,27 @@ def symbol_needs_import(fullname, namespaces):
341
348
  return True
342
349
 
343
350
 
344
- class _UseChecker(object):
351
+ class _UseChecker:
345
352
  """
346
353
  An object that can check whether it was used.
347
354
  """
348
- used = False
349
355
 
350
- def __init__(self, name, source, lineno):
356
+ used: bool = False
357
+ name: str
358
+ source: str
359
+ lineno: int
360
+
361
+ def __init__(self, name: str, source: str, lineno: int):
351
362
  self.name = name
352
363
  self.source = source # generally an Import
353
364
  self.lineno = lineno
365
+ logger.debug("Create _UseChecker : %r", self)
366
+
367
+ def __repr__(self):
368
+ return f"<{type(self).__name__}: name:{self.name!r} source:{self.source!r} lineno:{self.lineno} used:{self.used}>"
354
369
 
355
370
 
356
- class _MissingImportFinder(object):
371
+ class _MissingImportFinder:
357
372
  """
358
373
  A helper class to be used only by `_find_missing_imports_in_ast`.
359
374
 
@@ -371,8 +386,14 @@ class _MissingImportFinder(object):
371
386
 
372
387
  """
373
388
 
374
- def __init__(self, scopestack, find_unused_imports=False,
375
- parse_docstrings=False):
389
+ scopestack: ScopeStack
390
+ _lineno: Optional[int]
391
+ missing_imports: List[Tuple[Optional[int], DottedIdentifier]]
392
+ parse_docstrings: bool
393
+ unused_imports: Optional[List[Tuple[int, str]]]
394
+ _deferred_load_checks: list[tuple[str, ScopeStack, Optional[int]]]
395
+
396
+ def __init__(self, scopestack, *, find_unused_imports:bool, parse_docstrings:bool):
376
397
  """
377
398
  Construct the AST visitor.
378
399
 
@@ -384,20 +405,27 @@ class _MissingImportFinder(object):
384
405
  # Create a stack of namespaces. The caller should pass in a list that
385
406
  # includes the globals dictionary. ScopeStack() will make sure this
386
407
  # includes builtins.
387
- scopestack = ScopeStack(scopestack)
408
+ _scopestack = ScopeStack(scopestack)
409
+
388
410
  # Add an empty namespace to the stack. This facilitates adding stuff
389
411
  # to scopestack[-1] without ever modifying user globals.
390
- scopestack = scopestack.with_new_scope()
391
- self.scopestack = scopestack
412
+ self.scopestack = _scopestack._with_new_scope(
413
+ include_class_scopes=False, new_class_scope=False, unhide_classdef=False
414
+ )
415
+
392
416
  # Create data structure to hold the result.
393
417
  # missing_imports is a list of (lineno, DottedIdentifier) tuples.
394
418
  self.missing_imports = []
419
+
395
420
  # unused_imports is a list of (lineno, Import) tuples, if enabled.
396
421
  self.unused_imports = [] if find_unused_imports else None
422
+
397
423
  self.parse_docstrings = parse_docstrings
424
+
398
425
  # Function bodies that we need to check after defining names in this
399
426
  # function scope.
400
427
  self._deferred_load_checks = []
428
+
401
429
  # Whether we're currently in a FunctionDef.
402
430
  self._in_FunctionDef = False
403
431
  # Current lineno.
@@ -419,7 +447,8 @@ class _MissingImportFinder(object):
419
447
  finally:
420
448
  self.scopestack = oldscopestack
421
449
 
422
- def scan_for_import_issues(self, codeblock):
450
+ def scan_for_import_issues(self, codeblock: PythonBlock):
451
+ assert isinstance(codeblock, PythonBlock)
423
452
  # See global `scan_for_import_issues`
424
453
  if not isinstance(codeblock, PythonBlock):
425
454
  codeblock = PythonBlock(codeblock)
@@ -429,6 +458,7 @@ class _MissingImportFinder(object):
429
458
  # references in doctests to be noted as missing-imports. For now we
430
459
  # just let the code accumulate into self.missing_imports and ignore
431
460
  # the result.
461
+ logger.debug("unused: %r", self.unused_imports)
432
462
  missing_imports = sorted(self.missing_imports)
433
463
  if self.parse_docstrings and self.unused_imports is not None:
434
464
  doctest_blocks = codeblock.get_doctests()
@@ -446,10 +476,8 @@ class _MissingImportFinder(object):
446
476
  # Currently we don't support the 'global' keyword anyway so
447
477
  # this doesn't matter yet, and it's uncommon to use 'global'
448
478
  # in a doctest, so this is low priority to fix.
449
- oldstack = self.scopestack
450
- self.scopestack = self.scopestack.with_new_scope()
451
- self._scan_node(block.ast_node)
452
- self.scopestack = oldstack
479
+ with self._NewScopeCtx(check_unused_imports=False):
480
+ self._scan_node(block.ast_node)
453
481
  # Find literal brace identifiers like "... `Foo` ...".
454
482
  # TODO: Do this inline: (1) faster; (2) can use proper scope of vars
455
483
  # Once we do that, use _check_load() with new args
@@ -532,17 +560,36 @@ class _MissingImportFinder(object):
532
560
 
533
561
 
534
562
  @contextlib.contextmanager
535
- def _NewScopeCtx(self, **kwargs):
563
+ def _NewScopeCtx(
564
+ self,
565
+ include_class_scopes=False,
566
+ new_class_scope=False,
567
+ unhide_classdef=False,
568
+ check_unused_imports=True,
569
+ ):
536
570
  """
537
571
  Context manager that temporarily pushes a new empty namespace onto the
538
572
  stack of namespaces.
539
573
  """
540
574
  prev_scopestack = self.scopestack
541
- new_scopestack = prev_scopestack.with_new_scope(**kwargs)
575
+ new_scopestack = prev_scopestack._with_new_scope(
576
+ include_class_scopes=include_class_scopes,
577
+ new_class_scope=new_class_scope,
578
+ unhide_classdef=unhide_classdef,
579
+ )
542
580
  self.scopestack = new_scopestack
543
581
  try:
544
582
  yield
545
583
  finally:
584
+ logger.debug("throwing last scope from scopestack: %r", new_scopestack[-1])
585
+ for name, use_checker in new_scopestack[-1].items():
586
+ if use_checker and use_checker.used == False and check_unused_imports:
587
+ logger.debug(
588
+ "unused checker %r scopestack_depth %r",
589
+ use_checker,
590
+ len(self.scopestack),
591
+ )
592
+ self.unused_imports.append((use_checker.lineno, use_checker.source))
546
593
  assert self.scopestack is new_scopestack
547
594
  self.scopestack = prev_scopestack
548
595
 
@@ -588,7 +635,7 @@ class _MissingImportFinder(object):
588
635
  return
589
636
  if (len(node.targets) == 1 and isinstance(node.targets[0], ast.Name)
590
637
  and node.targets[0].id == '__all__'):
591
- if not isinstance(node.value, ast.List):
638
+ if not isinstance(node.value, (ast.List, ast.Tuple)):
592
639
  logger.warning("Don't know how to handle __all__ as (%s)" % node.value)
593
640
  return
594
641
  if not all(_is_ast_str(e) for e in node.value.elts):
@@ -672,7 +719,7 @@ class _MissingImportFinder(object):
672
719
  self.visit(node.body)
673
720
  self._in_FunctionDef = old_in_FunctionDef
674
721
 
675
- def _visit_typecomment(self, typecomment):
722
+ def _visit_typecomment(self, typecomment: str) -> None:
676
723
  """
677
724
  Warning, when a type comment the node is a string, not an ast node.
678
725
  We also get two types of type comments:
@@ -695,6 +742,7 @@ class _MissingImportFinder(object):
695
742
  """
696
743
  if typecomment is None:
697
744
  return
745
+ node: Union[ast.Module, ast.FunctionType]
698
746
  if '->' in typecomment:
699
747
  node = ast.parse(typecomment, mode='func_type')
700
748
  else:
@@ -702,7 +750,7 @@ class _MissingImportFinder(object):
702
750
 
703
751
  self.visit(node)
704
752
 
705
- def visit_arguments(self, node):
753
+ def visit_arguments(self, node) -> None:
706
754
  assert node._fields == ('posonlyargs', 'args', 'vararg', 'kwonlyargs', 'kw_defaults', 'kwarg', 'defaults'), node._fields
707
755
  # Argument/parameter list. Note that the defaults should be
708
756
  # considered "Load"s from the upper scope, and the argument names are
@@ -733,7 +781,7 @@ class _MissingImportFinder(object):
733
781
  else:
734
782
  self._visit_Store(node.kwarg)
735
783
 
736
- def visit_ExceptHandler(self, node):
784
+ def visit_ExceptHandler(self, node) -> None:
737
785
  assert node._fields == ('type', 'name', 'body')
738
786
  if node.type:
739
787
  self.visit(node.type)
@@ -909,9 +957,8 @@ class _MissingImportFinder(object):
909
957
 
910
958
  def _visit_StoreImport(self, node, modulename):
911
959
  name = node.asname or node.name
912
- logger.debug("_visit_StoreImport(asname=%r,name=%r)",
913
- node.asname, node.name)
914
- is_star = node.name == '*'
960
+ logger.debug("_visit_StoreImport(asname=%r, name=%r)", node.asname, node.name)
961
+ is_star = node.name == "*"
915
962
  if is_star:
916
963
  logger.debug("Got star import: line %s: 'from %s import *'",
917
964
  self._lineno, modulename)
@@ -928,7 +975,12 @@ class _MissingImportFinder(object):
928
975
  value = _UseChecker(name, imp, self._lineno)
929
976
  self._visit_Store(name, value)
930
977
 
931
- def _visit_Store(self, fullname:str, value=None):
978
+ def _visit_Store(self, fullname: str, value: Optional[_UseChecker] = None):
979
+ """
980
+ Visit a Store action, check for unused import
981
+ and add current value to the last scope.
982
+ """
983
+ assert isinstance(value, (_UseChecker, type(None)))
932
984
  logger.debug("_visit_Store(%r)", fullname)
933
985
  if fullname is None:
934
986
  return
@@ -998,10 +1050,11 @@ class _MissingImportFinder(object):
998
1050
  # Don't call generic_visit(node) here. Reason: We already visit the
999
1051
  # parts above, if relevant.
1000
1052
 
1001
- def _visit_Load_defered_global(self, fullname):
1053
+ def _visit_Load_defered_global(self, fullname:str):
1002
1054
  """
1003
1055
  Some things will be resolved in global scope later.
1004
1056
  """
1057
+ assert isinstance(fullname, str), fullname
1005
1058
  logger.debug("_visit_Load_defered_global(%r)", fullname)
1006
1059
  if symbol_needs_import(fullname, self.scopestack):
1007
1060
  data = (fullname, self.scopestack, self._lineno)
@@ -1090,7 +1143,11 @@ class _MissingImportFinder(object):
1090
1143
  unused_imports.sort()
1091
1144
 
1092
1145
 
1093
- def scan_for_import_issues(codeblock, find_unused_imports=True, parse_docstrings=False):
1146
+ def scan_for_import_issues(
1147
+ codeblock: PythonBlock,
1148
+ find_unused_imports: bool = True,
1149
+ parse_docstrings: bool = False,
1150
+ ):
1094
1151
  """
1095
1152
  Find missing and unused imports, by lineno.
1096
1153
 
@@ -1117,7 +1174,7 @@ def scan_for_import_issues(codeblock, find_unused_imports=True, parse_docstrings
1117
1174
  ([], [(1, Import('import baz'))])
1118
1175
 
1119
1176
  """
1120
- logger.debug("scan_for_import_issues()")
1177
+ logger.debug("global scan_for_import_issues()")
1121
1178
  if not isinstance(codeblock, PythonBlock):
1122
1179
  codeblock = PythonBlock(codeblock)
1123
1180
  namespaces = ScopeStack([{}])
@@ -1148,7 +1205,10 @@ def _find_missing_imports_in_ast(node, namespaces):
1148
1205
  # Traverse the abstract syntax tree.
1149
1206
  if logger.debug_enabled:
1150
1207
  logger.debug("ast=%s", ast.dump(node))
1151
- return _MissingImportFinder(namespaces).find_missing_imports(node)
1208
+ return _MissingImportFinder(
1209
+ namespaces,
1210
+ find_unused_imports=False,
1211
+ parse_docstrings=False).find_missing_imports(node)
1152
1212
 
1153
1213
  # TODO: maybe we should replace _find_missing_imports_in_ast with
1154
1214
  # _find_missing_imports_in_code(compile(node)). The method of parsing opcodes
@@ -316,6 +316,14 @@ def _debug_exception(*exc_info, **kwargs):
316
316
  # will cause print_verbose_tb to include a line with just a colon.
317
317
  # TODO: avoid that line.
318
318
  exc_info = ("", "", exc_info)
319
+ if exc_info[1]:
320
+ # Explicitly set sys.last_value / sys.last_exc to ensure they are available
321
+ # in the debugger. One use case is that this allows users to call
322
+ # pyflyby.saveframe() within the debugger.
323
+ if sys.version_info < (3, 12):
324
+ sys.last_value = exc_info[1]
325
+ else:
326
+ sys.last_exc = exc_info[1]
319
327
 
320
328
  with _DebuggerCtx(tty=tty) as pdb:
321
329
  if debugger_attached:
@@ -49,6 +49,7 @@ from pyflyby._importclns import ImportSet, Import
49
49
 
50
50
  module_dict = {}
51
51
 
52
+ PYFLYBY_LAZY_LOAD_PREFIX = "from pyflyby_autoimport_"
52
53
 
53
54
  def add_import(names: str, code: str, *, strict: bool = True):
54
55
  """
@@ -110,8 +111,8 @@ def _add_import(ip, names: str, code: str) -> None:
110
111
  private version of add_import
111
112
  """
112
113
  assert ip is not None
113
- mang = "pyflyby_autoimport_" + names.replace(",", "_").replace(" ", "_")
114
- a: FrozenSet[Import] = ImportSet(f"from {mang} import {names}")._importset
114
+ mang = PYFLYBY_LAZY_LOAD_PREFIX + names.replace(",", "_").replace(" ", "_")
115
+ a: FrozenSet[Import] = ImportSet(f"{mang} import {names}")._importset
115
116
  b: FrozenSet[Import] = ip._auto_importer.db.known_imports._importset
116
117
  s_import: FrozenSet[Import] = a | b
117
118
 
@@ -2,6 +2,8 @@
2
2
  # Copyright (C) 2011, 2012, 2013, 2014, 2015 Karl Chen.
3
3
  # License: MIT http://opensource.org/licenses/MIT
4
4
 
5
+ from __future__ import annotations
6
+
5
7
 
6
8
 
7
9
  from collections import defaultdict
@@ -417,7 +419,7 @@ class ImportDB:
417
419
  return result
418
420
 
419
421
  @classmethod
420
- def interpret_arg(cls, arg, target_filename):
422
+ def interpret_arg(cls, arg, target_filename) -> ImportDB:
421
423
  if arg is None:
422
424
  return cls.get_default(target_filename)
423
425
  else:
@@ -560,9 +562,11 @@ class ImportDB:
560
562
  filenames = [filenames]
561
563
  for f in filenames:
562
564
  assert isinstance(f, Filename)
563
- logger.debug("ImportDB: loading [%s], mandatory=[%s]",
564
- ', '.join(map(str, filenames)),
565
- ', '.join(map(str, _mandatory_filenames_deprecated)))
565
+ logger.debug(
566
+ "ImportDB: loading %r, mandatory=%r",
567
+ [str(f) for f in filenames],
568
+ [str(f) for f in _mandatory_filenames_deprecated],
569
+ )
566
570
  if SUPPORT_DEPRECATED_BEHAVIOR:
567
571
  # Before 2014-10, pyflyby read the following:
568
572
  # * known_imports from $PYFLYBY_PATH/known_imports/**/*.py or
@@ -13,12 +13,12 @@ from pyflyby._parse import PythonBlock
13
13
  from pyflyby._util import ImportPathCtx, Inf, NullCtx, memoize
14
14
  import re
15
15
 
16
- from typing import Union
16
+ from typing import Union, Optional, Literal
17
17
 
18
18
  from textwrap import indent
19
19
 
20
20
 
21
- class SourceToSourceTransformationBase(object):
21
+ class SourceToSourceTransformationBase:
22
22
 
23
23
  input: PythonBlock
24
24
 
@@ -121,7 +121,7 @@ class SourceToSourceFileImportsTransformation(SourceToSourceTransformationBase):
121
121
  result = [block.pretty_print(params=params) for block in self.blocks]
122
122
  return FileText.concatenate(result)
123
123
 
124
- def find_import_block_by_lineno(self, lineno):
124
+ def find_import_block_by_lineno(self, lineno: int):
125
125
  """
126
126
  Find the import block containing the given line number.
127
127
 
@@ -312,11 +312,11 @@ def ImportPathForRelativeImportsCtx(codeblock):
312
312
 
313
313
 
314
314
  def fix_unused_and_missing_imports(
315
- codeblock: Union[PythonBlock, str],
316
- add_missing=True,
317
- remove_unused="AUTOMATIC",
318
- add_mandatory=True,
319
- db=None,
315
+ codeblock: Union[PythonBlock, str, Filename],
316
+ add_missing: bool = True,
317
+ remove_unused: Union[Literal["AUTOMATIC"], bool] = "AUTOMATIC",
318
+ add_mandatory: bool = True,
319
+ db: Optional[ImportDB] = None,
320
320
  params=None,
321
321
  ) -> PythonBlock:
322
322
  r"""
@@ -345,6 +345,7 @@ def fix_unused_and_missing_imports(
345
345
  :rtype:
346
346
  `PythonBlock`
347
347
  """
348
+ _codeblock: PythonBlock
348
349
  if isinstance(codeblock, Filename):
349
350
  _codeblock = PythonBlock(codeblock)
350
351
  if not isinstance(codeblock, PythonBlock):
@@ -393,7 +394,7 @@ def fix_unused_and_missing_imports(
393
394
  logger.error(
394
395
  "%s: couldn't remove import %r", filename, imp,)
395
396
  except LineNumberNotFoundError as e:
396
- logger.error(
397
+ logger.debug(
397
398
  "%s: unused import %r on line %d not global",
398
399
  filename, str(imp), e.args[0])
399
400
  else:
@@ -96,7 +96,7 @@ If <module_name> is ``None``, then there is no "from" clause; instead just::
96
96
  """
97
97
 
98
98
  @total_ordering
99
- class Import(object):
99
+ class Import:
100
100
  """
101
101
  Representation of the desire to import a single name into the current
102
102
  namespace.