pyflyby 1.9.0__tar.gz → 1.9.2__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 (149) hide show
  1. {pyflyby-1.9.0/lib/python/pyflyby.egg-info → pyflyby-1.9.2}/PKG-INFO +1 -1
  2. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/pyflyby/_autoimp.py +20 -7
  3. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/pyflyby/_cmdline.py +4 -4
  4. {pyflyby-1.9.0/lib/python → pyflyby-1.9.2/bin}/pyflyby/_file.py +31 -17
  5. {pyflyby-1.9.0/lib/python → pyflyby-1.9.2/bin}/pyflyby/_importclns.py +2 -2
  6. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/pyflyby/_imports2s.py +15 -11
  7. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/pyflyby/_importstmt.py +27 -20
  8. {pyflyby-1.9.0/lib/python → pyflyby-1.9.2/bin}/pyflyby/_modules.py +13 -11
  9. {pyflyby-1.9.0/lib/python → pyflyby-1.9.2/bin}/pyflyby/_parse.py +36 -12
  10. {pyflyby-1.9.0/lib/python → pyflyby-1.9.2/bin}/pyflyby/_py.py +18 -10
  11. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/pyflyby/_util.py +20 -42
  12. {pyflyby-1.9.0/lib/python → pyflyby-1.9.2/bin}/pyflyby/_version.py +1 -1
  13. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/tidy-imports +0 -4
  14. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby/_autoimp.py +20 -7
  15. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby/_cmdline.py +4 -4
  16. {pyflyby-1.9.0/bin → pyflyby-1.9.2/lib/python}/pyflyby/_file.py +31 -17
  17. {pyflyby-1.9.0/bin → pyflyby-1.9.2/lib/python}/pyflyby/_importclns.py +2 -2
  18. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby/_imports2s.py +15 -11
  19. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby/_importstmt.py +27 -20
  20. {pyflyby-1.9.0/bin → pyflyby-1.9.2/lib/python}/pyflyby/_modules.py +13 -11
  21. {pyflyby-1.9.0/bin → pyflyby-1.9.2/lib/python}/pyflyby/_parse.py +36 -12
  22. {pyflyby-1.9.0/bin → pyflyby-1.9.2/lib/python}/pyflyby/_py.py +18 -10
  23. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby/_util.py +20 -42
  24. {pyflyby-1.9.0/bin → pyflyby-1.9.2/lib/python}/pyflyby/_version.py +1 -1
  25. {pyflyby-1.9.0 → pyflyby-1.9.2/lib/python/pyflyby.egg-info}/PKG-INFO +1 -1
  26. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/test_imports2s.py +35 -0
  27. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/test_importstmt.py +10 -10
  28. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/test_interactive.py +3 -4
  29. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/test_modules.py +31 -17
  30. {pyflyby-1.9.0 → pyflyby-1.9.2}/.pyflyby +0 -0
  31. {pyflyby-1.9.0 → pyflyby-1.9.2}/LICENSE.txt +0 -0
  32. {pyflyby-1.9.0 → pyflyby-1.9.2}/MANIFEST.in +0 -0
  33. {pyflyby-1.9.0 → pyflyby-1.9.2}/README.rst +0 -0
  34. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/autoipython +0 -0
  35. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/autopython +0 -0
  36. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/collect-exports +0 -0
  37. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/collect-imports +0 -0
  38. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/create-imports +0 -0
  39. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/find-import +0 -0
  40. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/list-bad-xrefs +0 -0
  41. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/prune-broken-imports +0 -0
  42. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/py +0 -0
  43. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/pyflyby/__init__.py +0 -0
  44. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/pyflyby/__main__.py +0 -0
  45. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/pyflyby/_comms.py +0 -0
  46. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/pyflyby/_dbg.py +0 -0
  47. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/pyflyby/_docxref.py +0 -0
  48. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/pyflyby/_flags.py +0 -0
  49. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/pyflyby/_format.py +0 -0
  50. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/pyflyby/_idents.py +0 -0
  51. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/pyflyby/_importdb.py +0 -0
  52. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/pyflyby/_interactive.py +0 -0
  53. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/pyflyby/_livepatch.py +0 -0
  54. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/pyflyby/_log.py +0 -0
  55. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/pyflyby/autoimport.py +0 -0
  56. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/pyflyby/importdb.py +0 -0
  57. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/pyflyby-diff +0 -0
  58. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/reformat-imports +0 -0
  59. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/replace-star-imports +0 -0
  60. {pyflyby-1.9.0 → pyflyby-1.9.2}/bin/transform-imports +0 -0
  61. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/LICENSE.txt +0 -0
  62. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/Makefile +0 -0
  63. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/TODO.txt +0 -0
  64. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/__init__.py +0 -0
  65. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/api/api.rst +0 -0
  66. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/api/autoimp.rst +0 -0
  67. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/api/cmdline.rst +0 -0
  68. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/api/comms.rst +0 -0
  69. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/api/dbg.rst +0 -0
  70. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/api/file.rst +0 -0
  71. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/api/flags.rst +0 -0
  72. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/api/format.rst +0 -0
  73. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/api/idents.rst +0 -0
  74. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/api/importclns.rst +0 -0
  75. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/api/importdb.rst +0 -0
  76. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/api/imports2s.rst +0 -0
  77. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/api/importstmt.rst +0 -0
  78. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/api/interactive.rst +0 -0
  79. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/api/livepatch.rst +0 -0
  80. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/api/log.rst +0 -0
  81. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/api/modules.rst +0 -0
  82. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/api/parse.rst +0 -0
  83. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/api/py.rst +0 -0
  84. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/api/util.rst +0 -0
  85. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/cli/autoipython.rst +0 -0
  86. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/cli/cli.rst +0 -0
  87. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/cli/collect_exports.rst +0 -0
  88. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/cli/collect_imports.rst +0 -0
  89. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/cli/find_import.rst +0 -0
  90. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/cli/prune_broken_imports.rst +0 -0
  91. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/cli/py.rst +0 -0
  92. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/cli/pyflyby_diff.rst +0 -0
  93. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/cli/reformat_imports.rst +0 -0
  94. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/cli/replace_star_imports.rst +0 -0
  95. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/cli/tidy_imports.rst +0 -0
  96. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/cli/transform_imports.rst +0 -0
  97. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/conf.py +0 -0
  98. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/index.rst +0 -0
  99. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/make.bat +0 -0
  100. {pyflyby-1.9.0 → pyflyby-1.9.2}/doc/testing.txt +0 -0
  101. {pyflyby-1.9.0 → pyflyby-1.9.2}/etc/pyflyby/canonical.py +0 -0
  102. {pyflyby-1.9.0 → pyflyby-1.9.2}/etc/pyflyby/common.py +0 -0
  103. {pyflyby-1.9.0 → pyflyby-1.9.2}/etc/pyflyby/forget.py +0 -0
  104. {pyflyby-1.9.0 → pyflyby-1.9.2}/etc/pyflyby/mandatory.py +0 -0
  105. {pyflyby-1.9.0 → pyflyby-1.9.2}/etc/pyflyby/numpy.py +0 -0
  106. {pyflyby-1.9.0 → pyflyby-1.9.2}/etc/pyflyby/std.py +0 -0
  107. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/emacs/pyflyby.el +0 -0
  108. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby/__init__.py +0 -0
  109. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby/__main__.py +0 -0
  110. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby/_comms.py +0 -0
  111. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby/_dbg.py +0 -0
  112. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby/_docxref.py +0 -0
  113. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby/_flags.py +0 -0
  114. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby/_format.py +0 -0
  115. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby/_idents.py +0 -0
  116. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby/_importdb.py +0 -0
  117. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby/_interactive.py +0 -0
  118. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby/_livepatch.py +0 -0
  119. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby/_log.py +0 -0
  120. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby/autoimport.py +0 -0
  121. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby/importdb.py +0 -0
  122. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby.egg-info/SOURCES.txt +0 -0
  123. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby.egg-info/dependency_links.txt +0 -0
  124. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby.egg-info/entry_points.txt +0 -0
  125. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby.egg-info/requires.txt +0 -0
  126. {pyflyby-1.9.0 → pyflyby-1.9.2}/lib/python/pyflyby.egg-info/top_level.txt +0 -0
  127. {pyflyby-1.9.0 → pyflyby-1.9.2}/libexec/pyflyby/colordiff +0 -0
  128. {pyflyby-1.9.0 → pyflyby-1.9.2}/libexec/pyflyby/diff-colorize +0 -0
  129. {pyflyby-1.9.0 → pyflyby-1.9.2}/setup.cfg +0 -0
  130. {pyflyby-1.9.0 → pyflyby-1.9.2}/setup.py +0 -0
  131. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/__init__.py +0 -0
  132. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/test_0testconfig.py +0 -0
  133. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/test_autoimp.py +0 -0
  134. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/test_cmdline.py +0 -0
  135. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/test_docxref.py +0 -0
  136. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/test_file.py +0 -0
  137. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/test_flags.py +0 -0
  138. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/test_format.py +0 -0
  139. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/test_idents.py +0 -0
  140. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/test_importclns.py +0 -0
  141. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/test_importdb.py +0 -0
  142. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/test_jupyterlab_pyflyby.py +0 -0
  143. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/test_livepatch.py +0 -0
  144. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/test_parse.py +0 -0
  145. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/test_py.py +0 -0
  146. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/test_util.py +0 -0
  147. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/tests_sorts.py +0 -0
  148. {pyflyby-1.9.0 → pyflyby-1.9.2}/tests/xrefs.py +0 -0
  149. {pyflyby-1.9.0 → pyflyby-1.9.2}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyflyby
3
- Version: 1.9.0
3
+ Version: 1.9.2
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
@@ -22,7 +22,7 @@ from pyflyby._importdb import ImportDB
22
22
  from pyflyby._importstmt import Import
23
23
  from pyflyby._log import logger
24
24
  from pyflyby._modules import ModuleHandle
25
- from pyflyby._parse import PythonBlock, infer_compile_mode
25
+ from pyflyby._parse import PythonBlock, infer_compile_mode, _is_ast_str
26
26
 
27
27
  if sys.version_info >= (3, 12):
28
28
  ATTRIBUTE_NAME = "value"
@@ -34,6 +34,11 @@ if sys.version_info > (3, 11):
34
34
  else:
35
35
  LOAD_SHIFT = 0
36
36
 
37
+ if sys.version_info > (3, 11):
38
+ LOAD_SHIFT = 1
39
+ else:
40
+ LOAD_SHIFT = 0
41
+
37
42
  NoneType = type(None)
38
43
  EllipsisType = type(Ellipsis)
39
44
 
@@ -580,7 +585,7 @@ class _MissingImportFinder(object):
580
585
  if not isinstance(node.value, ast.List):
581
586
  logger.warning("Don't know how to handle __all__ as (%s)" % node.value)
582
587
  return
583
- if not all(isinstance(e, ast.Str) for e in node.value.elts):
588
+ if not all(_is_ast_str(e) for e in node.value.elts):
584
589
  logger.warning("Don't know how to handle __all__ with list elements other than str")
585
590
  return
586
591
  for e in node.value.elts:
@@ -588,7 +593,11 @@ class _MissingImportFinder(object):
588
593
 
589
594
  def visit_ClassDef(self, node):
590
595
  logger.debug("visit_ClassDef(%r)", node)
591
- assert node._fields == ('name', 'bases', 'keywords', 'body', 'decorator_list')
596
+ if sys.version_info > (3,12):
597
+ # we don't visit type_params, so autoimport won't work yet for type annotations
598
+ assert node._fields == ('name', 'bases', 'keywords', 'body', 'decorator_list', 'type_params'), node._fields
599
+ else:
600
+ assert node._fields == ('name', 'bases', 'keywords', 'body', 'decorator_list'), node._fields
592
601
  self.visit(node.bases)
593
602
  self.visit(node.decorator_list)
594
603
  # The class's name is only visible to others (not to the body to the
@@ -600,11 +609,11 @@ class _MissingImportFinder(object):
600
609
  if self._in_class_def == 0:
601
610
  self.scopestack._class_delayed[node.name] = None
602
611
  with self._NewScopeCtx(new_class_scope=True):
603
- if not self._in_class_def:
604
- self._in_class_def += 1
605
- self._visit_Store(node.name)
612
+ self._in_class_def += 1
613
+ self._visit_Store(node.name)
606
614
  self.visit(node.body)
607
615
  self._in_class_def -= 1
616
+ assert self._in_class_def >= 0
608
617
  self._remove_from_missing_imports(node.name)
609
618
  self._visit_Store(node.name)
610
619
 
@@ -619,7 +628,11 @@ class _MissingImportFinder(object):
619
628
  # scope.
620
629
  # - Store the name in the current scope (but not visibly to
621
630
  # args/decorator_list).
622
- assert node._fields == ('name', 'args', 'body', 'decorator_list', 'returns', 'type_comment'), node._fields
631
+ if sys.version_info > (3, 12):
632
+ # we don't visit type_params, so autoimport won't work yet for type annotations
633
+ assert node._fields == ('name', 'args', 'body', 'decorator_list', 'returns', 'type_comment', 'type_params'), node._fields
634
+ else:
635
+ assert node._fields == ('name', 'args', 'body', 'decorator_list', 'returns', 'type_comment'), node._fields
623
636
  with self._NewScopeCtx(include_class_scopes=True):
624
637
  self.visit(node.decorator_list)
625
638
  self.visit(node.args)
@@ -7,11 +7,11 @@
7
7
  import optparse
8
8
  import os
9
9
  import signal
10
- from six import reraise
11
- from six.moves import input
10
+ from builtins import input
12
11
  import sys
13
12
  from textwrap import dedent
14
13
  import traceback
14
+ from typing import List
15
15
 
16
16
 
17
17
  from pyflyby._file import (FileText, Filename, atomic_write_file,
@@ -376,7 +376,7 @@ class Modifier(object):
376
376
  f.close()
377
377
 
378
378
 
379
- def process_actions(filenames, actions, modify_function,
379
+ def process_actions(filenames:List[str], actions, modify_function,
380
380
  reraise_exceptions=()):
381
381
  errors = []
382
382
  def on_error_filename_arg(arg):
@@ -408,7 +408,7 @@ def process_actions(filenames, actions, modify_function,
408
408
  # Exception takes more than one argument
409
409
  pass
410
410
  if logger.debug_enabled:
411
- reraise(type_e, e, tb)
411
+ raise
412
412
  traceback.print_exception(type(e), e, tb)
413
413
  finally:
414
414
  tb = None # avoid refcycles involving tb
@@ -4,13 +4,14 @@
4
4
 
5
5
 
6
6
 
7
- from functools import total_ordering
7
+ from functools import total_ordering, cached_property
8
8
  import io
9
9
  import os
10
10
  import re
11
11
  import sys
12
+ from typing import Optional, Tuple
12
13
 
13
- from pyflyby._util import cached_attribute, cmp, memoize
14
+ from pyflyby._util import cmp, memoize
14
15
 
15
16
 
16
17
  class UnsafeFilenameError(ValueError):
@@ -28,6 +29,8 @@ class Filename(object):
28
29
  Filename('/etc/passwd')
29
30
 
30
31
  """
32
+ _filename: str
33
+
31
34
  def __new__(cls, arg):
32
35
  if isinstance(arg, cls):
33
36
  return arg
@@ -85,7 +88,7 @@ class Filename(object):
85
88
  return NotImplemented
86
89
  return cmp(self._filename, o._filename)
87
90
 
88
- @cached_attribute
91
+ @cached_property
89
92
  def ext(self):
90
93
  """
91
94
  Returns the extension of this filename, including the dot.
@@ -99,15 +102,15 @@ class Filename(object):
99
102
  return None
100
103
  return dot + rhs
101
104
 
102
- @cached_attribute
105
+ @cached_property
103
106
  def base(self):
104
107
  return os.path.basename(self._filename)
105
108
 
106
- @cached_attribute
109
+ @cached_property
107
110
  def dir(self):
108
111
  return type(self)(os.path.dirname(self._filename))
109
112
 
110
- @cached_attribute
113
+ @cached_property
111
114
  def real(self):
112
115
  return type(self)(os.path.realpath(self._filename))
113
116
 
@@ -230,6 +233,9 @@ class FilePos(object):
230
233
  Both lineno and colno are 1-indexed.
231
234
  """
232
235
 
236
+ lineno: int
237
+ colno: int
238
+
233
239
  def __new__(cls, *args):
234
240
  if len(args) == 0:
235
241
  return cls._ONE_ONE
@@ -265,7 +271,7 @@ class FilePos(object):
265
271
  raise TypeError("Expected (int,int); got %r" % (args,))
266
272
 
267
273
  @classmethod
268
- def _from_lc(cls, lineno, colno):
274
+ def _from_lc(cls, lineno:int, colno:int):
269
275
  self = object.__new__(cls)
270
276
  self.lineno = lineno
271
277
  self.colno = colno
@@ -338,6 +344,10 @@ class FileText:
338
344
  Represents a contiguous sequence of lines from a file.
339
345
  """
340
346
 
347
+ filename: Optional[Filename]
348
+ startpos: FilePos
349
+ _lines: Optional[Tuple[str, ...]] = None
350
+
341
351
  def __new__(cls, arg, filename=None, startpos=None):
342
352
  """
343
353
  Return a new ``FileText`` instance.
@@ -371,7 +381,7 @@ class FileText:
371
381
  return FileText(arg.__text__(), filename=filename, startpos=startpos)
372
382
  elif isinstance(arg, str):
373
383
  self = object.__new__(cls)
374
- self.joined = arg
384
+ self._lines = tuple(arg.split('\n'))
375
385
  else:
376
386
  raise TypeError("%s: unexpected %s"
377
387
  % (cls.__name__, type(arg).__name__))
@@ -383,19 +393,21 @@ class FileText:
383
393
  return self
384
394
 
385
395
  @classmethod
386
- def _from_lines(cls, lines, filename, startpos):
396
+ def _from_lines(cls, lines, filename: Optional[Filename], startpos: FilePos):
387
397
  assert type(lines) is tuple
388
398
  assert len(lines) > 0
389
399
  assert isinstance(lines[0], str)
390
400
  assert not lines[-1].endswith("\n")
401
+ assert isinstance(startpos, FilePos), repr(startpos)
402
+ assert isinstance(filename, (Filename, type(None))), repr(filename)
391
403
  self = object.__new__(cls)
392
- self.lines = lines
404
+ self._lines = tuple(lines)
393
405
  self.filename = filename
394
406
  self.startpos = startpos
395
407
  return self
396
408
 
397
- @cached_attribute
398
- def lines(self):
409
+ @cached_property
410
+ def lines(self) -> Tuple[str, ...]:
399
411
  r"""
400
412
  Lines that have been split by newline.
401
413
 
@@ -408,16 +420,19 @@ class FileText:
408
420
  :rtype:
409
421
  ``tuple`` of ``str``
410
422
  """
423
+ if self._lines is not None:
424
+ return self._lines
411
425
  # Used if only initialized with 'joined'.
412
426
  # We use str.split() instead of str.splitlines() because the latter
413
427
  # doesn't distinguish between strings that end in newline or not
414
428
  # (or requires extra work to process if we use splitlines(True)).
415
429
  return tuple(self.joined.split('\n'))
416
430
 
417
- @cached_attribute
418
- def joined(self): # used if only initialized with 'lines'
431
+ @cached_property
432
+ def joined(self) -> str:
419
433
  return '\n'.join(self.lines)
420
434
 
435
+
421
436
  @classmethod
422
437
  def from_filename(cls, filename):
423
438
  return cls.from_lines(Filename(filename))
@@ -435,13 +450,12 @@ class FileText:
435
450
  return self
436
451
  else:
437
452
  result = object.__new__(type(self))
438
- result.lines = self.lines
439
- result.joined = self.joined
453
+ result._lines = self._lines
440
454
  result.filename = filename
441
455
  result.startpos = startpos
442
456
  return result
443
457
 
444
- @cached_attribute
458
+ @cached_property
445
459
  def endpos(self):
446
460
  """
447
461
  The position after the last character in the text.
@@ -215,8 +215,8 @@ class ImportSet(object):
215
215
  """
216
216
  :return:
217
217
  (mapping from name to __future__ imports,
218
- mapping from name to non-'from' imports,
219
- mapping from name to 'from' imports)
218
+ mapping from name to non-'from' imports,
219
+ mapping from name to 'from' imports)
220
220
  """
221
221
  ftr_imports = defaultdict(set)
222
222
  pkg_imports = defaultdict(set)
@@ -3,7 +3,6 @@
3
3
  # License: MIT http://opensource.org/licenses/MIT
4
4
  from collections import defaultdict
5
5
 
6
- import isort
7
6
  from pyflyby._autoimp import scan_for_import_issues
8
7
  from pyflyby._file import FileText, Filename
9
8
  from pyflyby._flags import CompilerFlags
@@ -14,10 +13,12 @@ from pyflyby._log import logger
14
13
  from pyflyby._parse import PythonBlock
15
14
  from pyflyby._util import ImportPathCtx, Inf, NullCtx, memoize
16
15
  import re
17
- from six import exec_
18
16
 
19
17
 
20
18
  class SourceToSourceTransformationBase(object):
19
+
20
+ input: PythonBlock
21
+
21
22
  def __new__(cls, arg):
22
23
  if isinstance(arg, cls):
23
24
  return arg
@@ -52,11 +53,15 @@ class SourceToSourceTransformationBase(object):
52
53
 
53
54
 
54
55
  class SourceToSourceTransformation(SourceToSourceTransformationBase):
56
+
57
+ _output: PythonBlock
58
+
55
59
  def preprocess(self):
56
- self.output = self.input
60
+ assert isinstance(self.input, PythonBlock), self.input
61
+ self._output = self.input
57
62
 
58
63
  def pretty_print(self, params=None):
59
- return self.output.text
64
+ return self._output.text
60
65
 
61
66
 
62
67
  class SourceToSourceImportBlockTransformation(SourceToSourceTransformationBase):
@@ -195,12 +200,10 @@ class SourceToSourceFileImportsTransformation(SourceToSourceTransformationBase):
195
200
  # insert in the middle.
196
201
  self.blocks[:1] = (
197
202
  [SourceToSourceTransformation(
198
- PythonBlock.concatenate(statements[:idx],
199
- assume_contiguous=True))] +
203
+ PythonBlock.concatenate(statements[:idx]))] +
200
204
  blocks +
201
205
  [SourceToSourceTransformation(
202
- PythonBlock.concatenate(statements[idx:],
203
- assume_contiguous=True))])
206
+ PythonBlock.concatenate(statements[idx:]))])
204
207
  break
205
208
  else:
206
209
  # First block is entirely comments, so just insert after it.
@@ -214,7 +217,7 @@ class SourceToSourceFileImportsTransformation(SourceToSourceTransformationBase):
214
217
  """
215
218
  block = SourceToSourceImportBlockTransformation("")
216
219
  sepblock = SourceToSourceTransformation("")
217
- sepblock.output = PythonBlock("\n")
220
+ sepblock._output = PythonBlock("\n")
218
221
  self.insert_new_blocks_after_comments([block, sepblock])
219
222
  self.import_blocks.insert(0, block)
220
223
  return block
@@ -433,7 +436,7 @@ def remove_broken_imports(codeblock, params=None):
433
436
  for imp in list(block.importset.imports):
434
437
  ns = {}
435
438
  try:
436
- exec_(imp.pretty_print(), ns)
439
+ exec(imp.pretty_print(), ns)
437
440
  except Exception as e:
438
441
  logger.info("%s: Could not import %r; removing it: %s: %s",
439
442
  filename, imp.fullname, type(e).__name__, e)
@@ -540,6 +543,7 @@ def sort_imports(codeblock):
540
543
  :param codeblock:
541
544
  :return: codeblock
542
545
  """
546
+ import isort
543
547
  sorted_imports = isort.code(
544
548
  str(codeblock),
545
549
  # To sort all the import in lexicographic order
@@ -644,7 +648,7 @@ def transform_imports(codeblock, transformations, params=None):
644
648
  output_imports = [ transform_import(imp) for imp in input_imports ]
645
649
  block.importset = ImportSet(output_imports, ignore_shadowed=True)
646
650
  else:
647
- block.output = transform_block(block.input)
651
+ block._output = transform_block(block.input)
648
652
  return transformer.output(params=params)
649
653
 
650
654
 
@@ -15,17 +15,16 @@ from pyflyby._parse import PythonStatement
15
15
  from pyflyby._util import (Inf, cached_attribute, cmp,
16
16
  longest_common_prefix)
17
17
 
18
- from black import format_str, FileMode as Mode
19
- from black.files import find_pyproject_toml, parse_pyproject_toml
20
- from black.mode import TargetVersion
21
18
 
19
+ from typing import Dict, Tuple, Optional
22
20
 
23
- def read_black_config():
24
- """Read the black configuration from ``pyproject.toml``
25
21
 
26
22
 
27
- """
28
- pyproject_path = find_pyproject_toml('.')
23
+ def read_black_config() -> Dict:
24
+ """Read the black configuration from ``pyproject.toml``"""
25
+ from black.files import find_pyproject_toml, parse_pyproject_toml
26
+
27
+ pyproject_path = find_pyproject_toml((".",))
29
28
 
30
29
  raw_config = parse_pyproject_toml(pyproject_path) if pyproject_path else {}
31
30
 
@@ -348,24 +347,31 @@ class Import(object):
348
347
  return NotImplemented
349
348
  return self._data < other._data
350
349
 
351
- def _validate_alias(arg):
350
+ def _validate_alias(arg) -> Tuple[str, Optional[str]]:
352
351
  """
353
352
  Ensure each alias is a tuple (str, None|str), and return it.
354
353
 
355
354
  """
356
355
  assert isinstance(arg, tuple)
357
- a0,a1 = arg
356
+ # Pyright does not seem to be able to infer the length from a
357
+ # the unpacking.
358
+ assert len(arg) == 2
359
+ a0, a1 = arg
358
360
  assert isinstance(a0, str)
359
361
  assert isinstance(a1, (str, type(None)))
360
362
  return arg
361
363
 
362
364
  @total_ordering
363
- class ImportStatement(object):
365
+ class ImportStatement:
364
366
  """
365
367
  Token-level representation of an import statement containing multiple
366
368
  imports from a single module. Corresponds to an ``ast.ImportFrom`` or
367
369
  ``ast.Import``.
368
370
  """
371
+
372
+ aliases : Tuple[Tuple[str, Optional[str]],...]
373
+ fromname : Optional[str]
374
+
369
375
  def __new__(cls, arg):
370
376
  if isinstance(arg, cls):
371
377
  return arg
@@ -381,10 +387,10 @@ class ImportStatement(object):
381
387
  raise TypeError
382
388
 
383
389
  @classmethod
384
- def from_parts(cls, fromname, aliases):
385
- assert isinstance(aliases, list)
390
+ def from_parts(cls, fromname:Optional[str], aliases:Tuple[Tuple[str, Optional[str]],...]):
391
+ assert isinstance(aliases, tuple)
386
392
  assert len(aliases) > 0
387
-
393
+
388
394
  self = object.__new__(cls)
389
395
  self.fromname = fromname
390
396
  self.aliases = tuple(_validate_alias(a) for a in aliases)
@@ -440,7 +446,7 @@ class ImportStatement(object):
440
446
  raise NonImportStatementError(
441
447
  'Expected ImportStatement, got {node}'.format(node=node)
442
448
  )
443
- aliases = [ (alias.name, alias.asname) for alias in node.names ]
449
+ aliases = tuple( (alias.name, alias.asname) for alias in node.names )
444
450
  return cls.from_parts(fromname, aliases)
445
451
 
446
452
  @classmethod
@@ -463,7 +469,7 @@ class ImportStatement(object):
463
469
  raise ValueError(
464
470
  "Inconsistent module names %r" % (sorted(module_names),))
465
471
  fromname = list(module_names)[0]
466
- aliases = [ imp.split[1:] for imp in imports ]
472
+ aliases = tuple(imp.split[1:] for imp in imports)
467
473
  return cls.from_parts(fromname, aliases)
468
474
 
469
475
  @cached_attribute
@@ -529,12 +535,15 @@ class ImportStatement(object):
529
535
  return res
530
536
 
531
537
  @staticmethod
532
- def run_black(src_contents: str, params) -> str:
538
+ def run_black(src_contents: str, params:FormatParams) -> str:
533
539
  """Run the black formatter for the Python source code given as a string
534
540
 
535
541
  This is adapted from https://github.com/akaihola/darker
536
542
 
537
543
  """
544
+ from black import format_str, FileMode
545
+ from black.mode import TargetVersion
546
+
538
547
  black_config = read_black_config()
539
548
  mode = dict()
540
549
  if "line_length" in black_config:
@@ -568,14 +577,12 @@ class ImportStatement(object):
568
577
  # ``--skip-string-normalization``, but the parameter for
569
578
  # ``black.Mode`` needs to be the opposite boolean of
570
579
  # ``skip-string-normalization``, hence the inverse boolean
571
- mode["string_normalization"] = not black_config[
572
- "skip_string_normalization"
573
- ]
580
+ mode["string_normalization"] = not black_config["skip_string_normalization"]
574
581
 
575
582
  # The custom handling of empty and all-whitespace files below will be unnecessary if
576
583
  # https://github.com/psf/black/pull/2484 lands in Black.
577
584
  contents_for_black = src_contents
578
- return format_str(contents_for_black, mode=Mode(**mode))
585
+ return format_str(contents_for_black, mode=FileMode(**mode))
579
586
 
580
587
  @property
581
588
  def _data(self):
@@ -125,6 +125,8 @@ class ModuleHandle(object):
125
125
  A handle to a module.
126
126
  """
127
127
 
128
+ name: DottedIdentifier
129
+
128
130
  def __new__(cls, arg):
129
131
  if isinstance(arg, cls):
130
132
  return arg
@@ -206,14 +208,8 @@ class ModuleHandle(object):
206
208
  if self.parent and not self.parent.exists:
207
209
  return False
208
210
 
209
- # pkgutil.find_loader returns None for unimported Python 3
210
- # namespace packages, so prefer importlib
211
- try:
212
- import importlib.util
213
- find = importlib.util.find_spec
214
- except ImportError:
215
- import pkgutil
216
- find = pkgutil.find_loader
211
+ import importlib.util
212
+ find = importlib.util.find_spec
217
213
 
218
214
  try:
219
215
  pkg = find(name)
@@ -241,6 +237,7 @@ class ModuleHandle(object):
241
237
  # module, which may be undesirable.
242
238
  import pkgutil
243
239
  try:
240
+ #TODO: deprecated and will be removed in 3.14
244
241
  loader = pkgutil.get_loader(str(self.name))
245
242
  except ImportError:
246
243
  return None
@@ -303,7 +300,7 @@ class ModuleHandle(object):
303
300
  Enumerate the importable submodules of this module.
304
301
 
305
302
  >>> ModuleHandle("email").submodules # doctest:+ELLIPSIS
306
- (..., 'email.encoders', ..., 'email.mime', ...)
303
+ (..., ModuleHandle('email.encoders'), ..., ModuleHandle('email.mime'), ...)
307
304
 
308
305
  :rtype:
309
306
  ``tuple`` of `ModuleHandle` s
@@ -366,6 +363,7 @@ class ModuleHandle(object):
366
363
 
367
364
  # If __all__ is defined, try to use it
368
365
  all_is_good = False # pun intended
366
+ all_members = []
369
367
  if "__all__" in members:
370
368
  # Iterate through the nodes and reconstruct the
371
369
  # value of __all__
@@ -418,7 +416,7 @@ class ModuleHandle(object):
418
416
  members = [n for n in members if not n.startswith("_")]
419
417
 
420
418
  # Filter out artificially added "deep" members.
421
- members = [(n, None) for n in members if "." not in n]
419
+ members = tuple([(n, None) for n in members if "." not in n])
422
420
  if not members:
423
421
  return None
424
422
  return ImportSet(
@@ -482,7 +480,11 @@ class ModuleHandle(object):
482
480
  result = module.module
483
481
  except Exception as e:
484
482
  raise ImportError(e)
485
- for part, prefix in zip(identifier, prefixes(identifier))[1:]:
483
+ # TODO: as far as I can tell the code here is never reached, or haven't
484
+ # been in quite some time as the line below was invalid on Python 3 since 2011
485
+ # zip(...)[...] fails as zip is not indexable.
486
+ # the only place that seem to be using this method is XrefScanner.
487
+ for part, prefix in list(zip(identifier, prefixes(identifier)))[1:]:
486
488
  try:
487
489
  result = getattr(result, str(part))
488
490
  except Exception: