pyflyby 1.8.6__tar.gz → 1.8.7__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 (148) hide show
  1. {pyflyby-1.8.6/lib/python/pyflyby.egg-info → pyflyby-1.8.7}/PKG-INFO +2 -2
  2. {pyflyby-1.8.6 → pyflyby-1.8.7}/README.rst +1 -1
  3. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/_autoimp.py +7 -20
  4. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/_comms.py +15 -10
  5. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/_imports2s.py +44 -2
  6. {pyflyby-1.8.6/lib/python → pyflyby-1.8.7/bin}/pyflyby/_parse.py +25 -92
  7. {pyflyby-1.8.6/lib/python → pyflyby-1.8.7/bin}/pyflyby/_py.py +21 -36
  8. {pyflyby-1.8.6/lib/python → pyflyby-1.8.7/bin}/pyflyby/_version.py +1 -1
  9. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/tidy-imports +10 -7
  10. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/_autoimp.py +7 -20
  11. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/_comms.py +15 -10
  12. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/_imports2s.py +44 -2
  13. {pyflyby-1.8.6/bin → pyflyby-1.8.7/lib/python}/pyflyby/_parse.py +25 -92
  14. {pyflyby-1.8.6/bin → pyflyby-1.8.7/lib/python}/pyflyby/_py.py +21 -36
  15. {pyflyby-1.8.6/bin → pyflyby-1.8.7/lib/python}/pyflyby/_version.py +1 -1
  16. {pyflyby-1.8.6 → pyflyby-1.8.7/lib/python/pyflyby.egg-info}/PKG-INFO +2 -2
  17. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby.egg-info/requires.txt +1 -0
  18. {pyflyby-1.8.6 → pyflyby-1.8.7}/setup.py +1 -1
  19. {pyflyby-1.8.6 → pyflyby-1.8.7}/tests/test_autoimp.py +2 -25
  20. {pyflyby-1.8.6 → pyflyby-1.8.7}/tests/test_cmdline.py +62 -0
  21. {pyflyby-1.8.6 → pyflyby-1.8.7}/tests/test_interactive.py +8 -10
  22. {pyflyby-1.8.6 → pyflyby-1.8.7}/tests/test_parse.py +12 -24
  23. {pyflyby-1.8.6 → pyflyby-1.8.7}/.pyflyby +0 -0
  24. {pyflyby-1.8.6 → pyflyby-1.8.7}/LICENSE.txt +0 -0
  25. {pyflyby-1.8.6 → pyflyby-1.8.7}/MANIFEST.in +0 -0
  26. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/autoipython +0 -0
  27. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/autopython +0 -0
  28. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/collect-exports +0 -0
  29. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/collect-imports +0 -0
  30. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/create-imports +0 -0
  31. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/find-import +0 -0
  32. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/list-bad-xrefs +0 -0
  33. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/prune-broken-imports +0 -0
  34. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/py +0 -0
  35. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/__init__.py +0 -0
  36. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/__main__.py +0 -0
  37. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/_cmdline.py +0 -0
  38. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/_dbg.py +0 -0
  39. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/_docxref.py +0 -0
  40. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/_file.py +0 -0
  41. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/_flags.py +0 -0
  42. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/_format.py +0 -0
  43. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/_idents.py +0 -0
  44. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/_importclns.py +0 -0
  45. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/_importdb.py +0 -0
  46. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/_importstmt.py +0 -0
  47. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/_interactive.py +0 -0
  48. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/_livepatch.py +0 -0
  49. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/_log.py +0 -0
  50. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/_modules.py +0 -0
  51. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/_util.py +0 -0
  52. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/autoimport.py +0 -0
  53. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby/importdb.py +0 -0
  54. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/pyflyby-diff +0 -0
  55. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/reformat-imports +0 -0
  56. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/replace-star-imports +0 -0
  57. {pyflyby-1.8.6 → pyflyby-1.8.7}/bin/transform-imports +0 -0
  58. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/LICENSE.txt +0 -0
  59. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/Makefile +0 -0
  60. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/TODO.txt +0 -0
  61. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/__init__.py +0 -0
  62. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/api/api.rst +0 -0
  63. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/api/autoimp.rst +0 -0
  64. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/api/cmdline.rst +0 -0
  65. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/api/comms.rst +0 -0
  66. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/api/dbg.rst +0 -0
  67. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/api/file.rst +0 -0
  68. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/api/flags.rst +0 -0
  69. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/api/format.rst +0 -0
  70. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/api/idents.rst +0 -0
  71. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/api/importclns.rst +0 -0
  72. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/api/importdb.rst +0 -0
  73. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/api/imports2s.rst +0 -0
  74. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/api/importstmt.rst +0 -0
  75. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/api/interactive.rst +0 -0
  76. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/api/livepatch.rst +0 -0
  77. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/api/log.rst +0 -0
  78. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/api/modules.rst +0 -0
  79. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/api/parse.rst +0 -0
  80. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/api/py.rst +0 -0
  81. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/api/util.rst +0 -0
  82. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/cli/autoipython.rst +0 -0
  83. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/cli/cli.rst +0 -0
  84. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/cli/collect_exports.rst +0 -0
  85. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/cli/collect_imports.rst +0 -0
  86. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/cli/find_import.rst +0 -0
  87. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/cli/prune_broken_imports.rst +0 -0
  88. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/cli/py.rst +0 -0
  89. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/cli/pyflyby_diff.rst +0 -0
  90. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/cli/reformat_imports.rst +0 -0
  91. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/cli/replace_star_imports.rst +0 -0
  92. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/cli/tidy_imports.rst +0 -0
  93. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/cli/transform_imports.rst +0 -0
  94. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/conf.py +0 -0
  95. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/index.rst +0 -0
  96. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/make.bat +0 -0
  97. {pyflyby-1.8.6 → pyflyby-1.8.7}/doc/testing.txt +0 -0
  98. {pyflyby-1.8.6 → pyflyby-1.8.7}/etc/pyflyby/canonical.py +0 -0
  99. {pyflyby-1.8.6 → pyflyby-1.8.7}/etc/pyflyby/common.py +0 -0
  100. {pyflyby-1.8.6 → pyflyby-1.8.7}/etc/pyflyby/forget.py +0 -0
  101. {pyflyby-1.8.6 → pyflyby-1.8.7}/etc/pyflyby/mandatory.py +0 -0
  102. {pyflyby-1.8.6 → pyflyby-1.8.7}/etc/pyflyby/numpy.py +0 -0
  103. {pyflyby-1.8.6 → pyflyby-1.8.7}/etc/pyflyby/std.py +0 -0
  104. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/emacs/pyflyby.el +0 -0
  105. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/__init__.py +0 -0
  106. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/__main__.py +0 -0
  107. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/_cmdline.py +0 -0
  108. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/_dbg.py +0 -0
  109. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/_docxref.py +0 -0
  110. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/_file.py +0 -0
  111. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/_flags.py +0 -0
  112. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/_format.py +0 -0
  113. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/_idents.py +0 -0
  114. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/_importclns.py +0 -0
  115. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/_importdb.py +0 -0
  116. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/_importstmt.py +0 -0
  117. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/_interactive.py +0 -0
  118. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/_livepatch.py +0 -0
  119. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/_log.py +0 -0
  120. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/_modules.py +0 -0
  121. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/_util.py +0 -0
  122. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/autoimport.py +0 -0
  123. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby/importdb.py +0 -0
  124. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby.egg-info/SOURCES.txt +0 -0
  125. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby.egg-info/dependency_links.txt +0 -0
  126. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby.egg-info/entry_points.txt +0 -0
  127. {pyflyby-1.8.6 → pyflyby-1.8.7}/lib/python/pyflyby.egg-info/top_level.txt +0 -0
  128. {pyflyby-1.8.6 → pyflyby-1.8.7}/libexec/pyflyby/colordiff +0 -0
  129. {pyflyby-1.8.6 → pyflyby-1.8.7}/libexec/pyflyby/diff-colorize +0 -0
  130. {pyflyby-1.8.6 → pyflyby-1.8.7}/setup.cfg +0 -0
  131. {pyflyby-1.8.6 → pyflyby-1.8.7}/tests/__init__.py +0 -0
  132. {pyflyby-1.8.6 → pyflyby-1.8.7}/tests/test_0testconfig.py +0 -0
  133. {pyflyby-1.8.6 → pyflyby-1.8.7}/tests/test_docxref.py +0 -0
  134. {pyflyby-1.8.6 → pyflyby-1.8.7}/tests/test_file.py +0 -0
  135. {pyflyby-1.8.6 → pyflyby-1.8.7}/tests/test_flags.py +0 -0
  136. {pyflyby-1.8.6 → pyflyby-1.8.7}/tests/test_format.py +0 -0
  137. {pyflyby-1.8.6 → pyflyby-1.8.7}/tests/test_idents.py +0 -0
  138. {pyflyby-1.8.6 → pyflyby-1.8.7}/tests/test_importclns.py +0 -0
  139. {pyflyby-1.8.6 → pyflyby-1.8.7}/tests/test_importdb.py +0 -0
  140. {pyflyby-1.8.6 → pyflyby-1.8.7}/tests/test_imports2s.py +0 -0
  141. {pyflyby-1.8.6 → pyflyby-1.8.7}/tests/test_importstmt.py +0 -0
  142. {pyflyby-1.8.6 → pyflyby-1.8.7}/tests/test_jupyterlab_pyflyby.py +0 -0
  143. {pyflyby-1.8.6 → pyflyby-1.8.7}/tests/test_livepatch.py +0 -0
  144. {pyflyby-1.8.6 → pyflyby-1.8.7}/tests/test_modules.py +0 -0
  145. {pyflyby-1.8.6 → pyflyby-1.8.7}/tests/test_py.py +0 -0
  146. {pyflyby-1.8.6 → pyflyby-1.8.7}/tests/test_util.py +0 -0
  147. {pyflyby-1.8.6 → pyflyby-1.8.7}/tests/xrefs.py +0 -0
  148. {pyflyby-1.8.6 → pyflyby-1.8.7}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyflyby
3
- Version: 1.8.6
3
+ Version: 1.8.7
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
@@ -257,7 +257,7 @@ Compatibility
257
257
  -------------
258
258
 
259
259
  Tested with:
260
- - Python 3.7, 3.8, 3.9, 3.10
260
+ - Python 3.8, 3.9, 3.10
261
261
  - IPython 0.10, 0.11, 0.12, 0.13, 1.0, 1.2, 2.0, 2.1, 2.2, 2.3, 2.4, 3.0,
262
262
  3.1, 3.2, 4.0., 7.11 (latest)
263
263
  - IPython (text console), IPython Notebook, Spyder
@@ -236,7 +236,7 @@ Compatibility
236
236
  -------------
237
237
 
238
238
  Tested with:
239
- - Python 3.7, 3.8, 3.9, 3.10
239
+ - Python 3.8, 3.9, 3.10
240
240
  - IPython 0.10, 0.11, 0.12, 0.13, 1.0, 1.2, 2.0, 2.1, 2.2, 2.3, 2.4, 3.0,
241
241
  3.1, 3.2, 4.0., 7.11 (latest)
242
242
  - IPython (text console), IPython Notebook, Spyder
@@ -609,17 +609,13 @@ class _MissingImportFinder(object):
609
609
  # scope.
610
610
  # - Store the name in the current scope (but not visibly to
611
611
  # args/decorator_list).
612
- if sys.version_info >= (3, 8):
613
- assert node._fields == ('name', 'args', 'body', 'decorator_list', 'returns', 'type_comment'), node._fields
614
- else:
615
- assert node._fields == ('name', 'args', 'body', 'decorator_list', 'returns'), node._fields
612
+ assert node._fields == ('name', 'args', 'body', 'decorator_list', 'returns', 'type_comment'), node._fields
616
613
  with self._NewScopeCtx(include_class_scopes=True):
617
614
  self.visit(node.args)
618
615
  self.visit(node.decorator_list)
619
616
  if node.returns:
620
617
  self.visit(node.returns)
621
- if sys.version_info >= (3, 8):
622
- self._visit_typecomment(node.type_comment)
618
+ self._visit_typecomment(node.type_comment)
623
619
  old_in_FunctionDef = self._in_FunctionDef
624
620
  self._in_FunctionDef = True
625
621
  with self._NewScopeCtx(unhide_classdef=True):
@@ -671,10 +667,7 @@ class _MissingImportFinder(object):
671
667
  self.visit(node)
672
668
 
673
669
  def visit_arguments(self, node):
674
- if sys.version_info >= (3, 8):
675
- assert node._fields == ('posonlyargs', 'args', 'vararg', 'kwonlyargs', 'kw_defaults', 'kwarg', 'defaults'), node._fields
676
- else:
677
- assert node._fields == ('args', 'vararg', 'kwonlyargs', 'kw_defaults', 'kwarg', 'defaults'), node._fields
670
+ assert node._fields == ('posonlyargs', 'args', 'vararg', 'kwonlyargs', 'kw_defaults', 'kwarg', 'defaults'), node._fields
678
671
  # Argument/parameter list. Note that the defaults should be
679
672
  # considered "Load"s from the upper scope, and the argument names are
680
673
  # "Store"s in the function scope.
@@ -693,8 +686,7 @@ class _MissingImportFinder(object):
693
686
  # Store arg names.
694
687
  self.visit(node.args)
695
688
  self.visit(node.kwonlyargs)
696
- if sys.version_info >= (3, 8):
697
- self.visit(node.posonlyargs)
689
+ self.visit(node.posonlyargs)
698
690
  # may be None.
699
691
  if node.vararg:
700
692
  self.visit(node.vararg)
@@ -807,16 +799,12 @@ class _MissingImportFinder(object):
807
799
  self._visit_fullname(node.id, node.ctx)
808
800
 
809
801
  def visit_arg(self, node):
810
- if sys.version_info >= (3, 8):
811
- assert node._fields == ('arg', 'annotation', 'type_comment'), node._fields
812
- else:
813
- assert node._fields == ('arg', 'annotation'), node._fields
802
+ assert node._fields == ('arg', 'annotation', 'type_comment'), node._fields
814
803
  if node.annotation:
815
804
  self.visit(node.annotation)
816
805
  # Treat it like a Name node would from Python 2
817
806
  self._visit_fullname(node.arg, ast.Param())
818
- if sys.version_info >= (3, 8):
819
- self._visit_typecomment(node.type_comment)
807
+ self._visit_typecomment(node.type_comment)
820
808
 
821
809
  def visit_Attribute(self, node):
822
810
  name_revparts = []
@@ -1512,8 +1500,7 @@ def find_missing_imports(arg, namespaces):
1512
1500
  else:
1513
1501
  return []
1514
1502
  # Parse the string into an AST.
1515
- kw = {} if sys.version_info < (3, 8) else {'type_comments': True}
1516
- node = ast.parse(arg, **kw) # may raise SyntaxError
1503
+ node = ast.parse(arg, type_comments=True) # may raise SyntaxError
1517
1504
  # Get missing imports from AST.
1518
1505
  return _find_missing_imports_in_ast(node, namespaces)
1519
1506
  elif isinstance(arg, PythonBlock):
@@ -121,24 +121,23 @@ def _reformat_helper(input_code, imports):
121
121
 
122
122
  def extract_import_statements(text):
123
123
  """This is a util for notebook interactions and extracts import statements
124
- from some python code.
125
-
124
+ from some python code. This function also re-orders imports.
126
125
  Args:
127
126
  code (str): The code from which import statements have to be extracted
128
127
 
129
128
  Returns:
130
- (list[str], str): The first returned value is a list of all import
131
- statements. The second returned value is the remaining code after
129
+ (str, str): The first returned value contains all the import statements.
130
+ The second returned value is the remaining code after
132
131
  extracting the import statements.
133
132
  """
134
133
  transformer = SourceToSourceFileImportsTransformation(text)
135
- imports = [str(im.pretty_print()) for im in transformer.import_blocks]
134
+ imports = '\n'.join([str(im.pretty_print()) for im in transformer.import_blocks])
136
135
  remaining_code = "\n".join([str(st.pretty_print()) if not isinstance(st, SourceToSourceImportBlockTransformation) else "" for st in transformer.blocks])
137
136
  return imports, remaining_code
138
137
 
139
- def collect_code_with_imports_on_top(imports, cell_array):
140
- return (
141
- "\n".join(imports)
138
+ def collect_code_with_imports_on_top(imports: str, cell_array):
139
+ return (
140
+ imports
142
141
  + "\n"
143
142
  + "\n".join(
144
143
  [
@@ -185,7 +184,13 @@ def comm_open_handler(comm, message):
185
184
  elif data["type"] == TIDY_IMPORTS:
186
185
  checksum = data.get("checksum", '')
187
186
  cell_array = data.get("cellArray", [])
188
- import_statements, processed_cell_array = [], []
187
+ # import_statements is a string because when
188
+ # SourceToSourceFileImportsTransformation is run on a piece of code
189
+ # it will club similar imports together and re-order the imports
190
+ # by making the imports a string, all the imports are processed
191
+ # together making sure tidy-imports has context on all the imports
192
+ # while clubbing similar imports and re-ordering them.
193
+ import_statements, processed_cell_array = "", []
189
194
  for cell in cell_array:
190
195
  text = cell.get("text")
191
196
  cell_type = cell.get("type")
@@ -201,6 +206,6 @@ def comm_open_handler(comm, message):
201
206
  "checksum": checksum,
202
207
  "type": TIDY_IMPORTS,
203
208
  "cells": processed_cell_array,
204
- "imports": [im.strip() for im in import_statements],
209
+ "imports": import_statements,
205
210
  }
206
211
  )
@@ -1,9 +1,9 @@
1
1
  # pyflyby/_imports2s.py.
2
2
  # Copyright (C) 2011-2018 Karl Chen.
3
3
  # License: MIT http://opensource.org/licenses/MIT
4
+ from collections import defaultdict
4
5
 
5
-
6
-
6
+ import isort
7
7
  from pyflyby._autoimp import scan_for_import_issues
8
8
  from pyflyby._file import FileText, Filename
9
9
  from pyflyby._flags import CompilerFlags
@@ -534,6 +534,48 @@ def replace_star_imports(codeblock, params=None):
534
534
  return transformer.output(params=params)
535
535
 
536
536
 
537
+ def sort_imports(codeblock):
538
+ """
539
+ Sort imports for better grouping.
540
+ :param codeblock:
541
+ :return: codeblock
542
+ """
543
+ sorted_imports = isort.code(
544
+ str(codeblock),
545
+ # To sort all the import in lexicographic order
546
+ force_sort_within_sections=True,
547
+ # This is done below
548
+ lines_between_sections=0,
549
+ lines_after_imports=1
550
+ )
551
+ # Step 1: Split the input string into a list of lines
552
+ lines = sorted_imports.split('\n')
553
+
554
+ # Step 2: Identify groups of imports and keep track of their line numbers
555
+ pkg_lines = defaultdict(list)
556
+ line_pkg_dict = {}
557
+ for i, line in enumerate(lines):
558
+ match = re.match(r'(from (\w+)|import (\w+))', line)
559
+ if match:
560
+ current_pkg = match.groups()[1:3]
561
+ current_pkg = current_pkg[0] if current_pkg[0] is not None else current_pkg[1]
562
+ pkg_lines[current_pkg].append(i)
563
+ line_pkg_dict[i] = current_pkg
564
+
565
+ # Step 3: Create the output list of lines with blank lines around groups with more than one import
566
+ output_lines = []
567
+ for i, line in enumerate(lines):
568
+ if i > 0 and line_pkg_dict.get(i) != line_pkg_dict.get(i-1) and len(pkg_lines[line_pkg_dict.get(i)]) > 1:
569
+ output_lines.append('')
570
+ output_lines.append(line)
571
+ if i < len(lines) - 1 and line_pkg_dict.get(i) != line_pkg_dict.get(i+1) and len(pkg_lines[line_pkg_dict.get(i)]) > 1:
572
+ output_lines.append('')
573
+
574
+ # Step 4: Join the lines to create the output string
575
+ sorted_output_str = '\n'.join(output_lines)
576
+ return PythonBlock(sorted_output_str)
577
+
578
+
537
579
  def transform_imports(codeblock, transformations, params=None):
538
580
  """
539
581
  Transform imports as specified by ``transformations``.
@@ -24,19 +24,7 @@ from pyflyby._util import cached_attribute, cmp
24
24
 
25
25
  from ast import Bytes
26
26
 
27
- if sys.version_info >= (3, 8):
28
- from ast import TypeIgnore, AsyncFunctionDef
29
- else:
30
-
31
- # TypeIgnore, AsyncFunctionDef does not exist on Python 3.7 and before. thus
32
- # we define a dummy TypeIgnore, AsyncFunctionDef just to simplify remaining
33
- # code.
34
-
35
- class TypeIgnore:
36
- pass
37
-
38
- class AsyncFunctionDef:
39
- pass
27
+ from ast import TypeIgnore, AsyncFunctionDef
40
28
 
41
29
 
42
30
  def _is_comment_or_blank(line):
@@ -104,37 +92,27 @@ def _iter_child_nodes_in_order_internal_1(node):
104
92
  assert node._fields == ("keys", "values")
105
93
  yield list(zip(node.keys, node.values))
106
94
  elif isinstance(node, (ast.FunctionDef, AsyncFunctionDef)):
107
- if sys.version_info >= (3, 8):
108
- assert node._fields == (
109
- "name",
110
- "args",
111
- "body",
112
- "decorator_list",
113
- "returns",
114
- "type_comment",
115
- ), node._fields
116
- res = (
117
- node.type_comment,
118
- node.decorator_list,
119
- node.args,
120
- node.returns,
121
- node.body,
122
- )
123
- yield res
124
- else:
125
- assert node._fields == ('name', 'args', 'body', 'decorator_list',
126
- 'returns'), node._fields
127
- yield node.decorator_list, node.args, node.returns, node.body
95
+ assert node._fields == (
96
+ "name",
97
+ "args",
98
+ "body",
99
+ "decorator_list",
100
+ "returns",
101
+ "type_comment",
102
+ ), node._fields
103
+ res = (
104
+ node.type_comment,
105
+ node.decorator_list,
106
+ node.args,
107
+ node.returns,
108
+ node.body,
109
+ )
110
+ yield res
128
111
  # node.name is a string, not an AST node
129
112
  elif isinstance(node, ast.arguments):
130
- if sys.version_info >= (3, 8):
131
- assert node._fields == ('posonlyargs', 'args', 'vararg', 'kwonlyargs',
132
- 'kw_defaults', 'kwarg', 'defaults'), node._fields
133
- args = node.posonlyargs + node.args
134
- else:
135
- assert node._fields == ('args', 'vararg', 'kwonlyargs',
136
- 'kw_defaults', 'kwarg', 'defaults'), node._fields
137
- args = node.args
113
+ assert node._fields == ('posonlyargs', 'args', 'vararg', 'kwonlyargs',
114
+ 'kw_defaults', 'kwarg', 'defaults'), node._fields
115
+ args = node.posonlyargs + node.args
138
116
  defaults = node.defaults or ()
139
117
  num_no_default = len(args) - len(defaults)
140
118
  yield args[:num_no_default]
@@ -187,22 +165,10 @@ def _flags_to_try(source, flags, auto_flags, mode):
187
165
  If ``auto_flags`` is True, then yield ``flags`` and ``flags ^ print_function``.
188
166
  """
189
167
  flags = CompilerFlags(flags)
190
- if sys.version_info >= (3, 8):
191
- if re.search(r"# *type:", source):
192
- flags = flags | CompilerFlags('type_comments')
193
- yield flags
194
- return
195
- if not auto_flags:
196
- yield flags
197
- return
198
- if mode == "eval":
199
- if re.search(r"\bprint\b", source):
200
- flags = flags | CompilerFlags("print_function")
201
- yield flags
202
- return
168
+ if re.search(r"# *type:", source):
169
+ flags = flags | CompilerFlags('type_comments')
203
170
  yield flags
204
- if re.search(r"\bprint\b", source):
205
- yield flags ^ CompilerFlags("print_function")
171
+ return
206
172
 
207
173
 
208
174
  def _parse_ast_nodes(text, flags, auto_flags, mode):
@@ -348,14 +314,6 @@ def _annotate_ast_startpos(ast_node, parent_ast_node, minpos, text, flags):
348
314
  """
349
315
  assert isinstance(ast_node, (ast.AST, str, TypeIgnore)), ast_node
350
316
 
351
- # joined strings and children do not carry a column offset on pre-3.8
352
- # this prevent reformatting.
353
- # set the column offset to the parent value before 3.8
354
- if (3, 7) < sys.version_info < (3, 8):
355
- instances = (getattr(ast, "JoinedStr", None), ast.FormattedValue)
356
- if ((isinstance(ast_node, instances) or isinstance(parent_ast_node, instances)) and ast_node.col_offset == -1) or isinstance(ast_node, ast.keyword):
357
- ast_node.col_offset = parent_ast_node.col_offset
358
-
359
317
  # First, traverse child nodes. If the first child node (recursively) is a
360
318
  # multiline string, then we need to transfer its information to this node.
361
319
  # Walk all nodes/fields of the AST. We implement this as a custom
@@ -400,9 +358,8 @@ def _annotate_ast_startpos(ast_node, parent_ast_node, minpos, text, flags):
400
358
  if ast_node.col_offset >= 0:
401
359
  # In Python 3.8+, FunctionDef.lineno is the line with the def. To
402
360
  # account for decorators, we need the lineno of the first decorator
403
- if (sys.version_info >= (3, 8)
404
- and isinstance(ast_node, (ast.FunctionDef, ast.ClassDef, AsyncFunctionDef))
405
- and ast_node.decorator_list):
361
+ if (isinstance(ast_node, (ast.FunctionDef, ast.ClassDef, AsyncFunctionDef))
362
+ and ast_node.decorator_list):
406
363
  delta = (ast_node.decorator_list[0].lineno-1,
407
364
  # The col_offset doesn't include the @
408
365
  ast_node.decorator_list[0].col_offset - 1)
@@ -486,9 +443,6 @@ def _annotate_ast_startpos(ast_node, parent_ast_node, minpos, text, flags):
486
443
  for _m in re.finditer("[bBrRuU]*[\"\']", start_line)])
487
444
  target_str = ast_node.s
488
445
 
489
- if isinstance(target_str, bytes) and sys.version_info[:2] == (3, 7):
490
- target_str = target_str.decode()
491
-
492
446
  # Loop over possible end_linenos. The first one we've identified is the
493
447
  # by far most likely one, but in theory it could be anywhere later in the
494
448
  # file. This could be because of a dastardly concatenated string like
@@ -553,18 +507,8 @@ def _annotate_ast_startpos(ast_node, parent_ast_node, minpos, text, flags):
553
507
  candidate_str = _test_parse_string_literal(subtext, flags)
554
508
  if candidate_str is None:
555
509
  continue
556
- if isinstance(candidate_str, bytes) and sys.version_info[:2] == (3, 7):
557
- candidate_str = candidate_str.decode()
558
510
 
559
511
  maybe_fstring = False
560
- try:
561
- if (3, 7) <= sys.version_info <= (3, 8):
562
- potential_start = text.lines[startpos.lineno - 1]
563
- maybe_fstring = ("f'" in potential_start) or (
564
- 'f"' in potential_start
565
- )
566
- except IndexError:
567
- pass
568
512
 
569
513
  if target_str == candidate_str and target_str:
570
514
  # Success!
@@ -589,17 +533,6 @@ def _annotate_ast_startpos(ast_node, parent_ast_node, minpos, text, flags):
589
533
  for (sq, sp) in startpos_candidates
590
534
  if sp in matched_prefix
591
535
  ]
592
- if (3, 7) <= sys.version_info <= (3, 8):
593
- if len(f_string_candidate_prefixes) == 1:
594
- # we did not find the string but there is one fstring candidate starting it
595
-
596
- ast_node.startpos, ast_node.endpos = f_string_candidate_prefixes[0]
597
- return True
598
- elif isinstance(parent_ast_node, ast.JoinedStr):
599
- self_pos = parent_ast_node.values.index(ast_node)
600
- ast_node.startpos = parent_ast_node.values[self_pos - 1].startpos
601
- ast_node.endpos = parent_ast_node.values[self_pos - 1].endpos
602
- return True
603
536
  raise ValueError("Couldn't find exact position of %s" % (ast.dump(ast_node)))
604
537
 
605
538
 
@@ -379,10 +379,7 @@ FLAGS = CompilerFlags(["absolute_import", "with_statement", "division",
379
379
 
380
380
 
381
381
  def _get_argspec(arg, _recurse=False):
382
- if sys.version_info[0] == 3:
383
- from inspect import getfullargspec as getargspec, FullArgSpec as ArgSpec
384
- else:
385
- from inspect import getargspec, ArgSpec
382
+ from inspect import getfullargspec as getargspec, FullArgSpec as ArgSpec
386
383
  if isinstance(arg, FunctionType):
387
384
  return getargspec(arg)
388
385
  elif isinstance(arg, MethodType):
@@ -402,10 +399,7 @@ def _get_argspec(arg, _recurse=False):
402
399
  return _get_argspec(arg.__call__, _recurse=False)
403
400
  elif callable(arg):
404
401
  # Unknown, probably a built-in method.
405
- if sys.version_info[0] == 3:
406
- return ArgSpec((), "args", "kwargs", None, [], None, {})
407
- else:
408
- return ArgSpec((), "args", "kwargs", None)
402
+ return ArgSpec((), "args", "kwargs", None, [], None, {})
409
403
  raise TypeError(
410
404
  "_get_argspec: unexpected %s" % (type(arg).__name__,))
411
405
 
@@ -515,12 +509,11 @@ def _build_function_usage_string(function_name, argspec, prefix):
515
509
  if argspec.varargs:
516
510
  syntax1 += " %s..." % argspec.varargs
517
511
  syntax1 += "]" * len(defaults)
518
- if sys.version_info[0] == 3:
519
- for arg in argspec.kwonlyargs:
520
- if argspec.kwonlydefaults and arg in argspec.kwonlydefaults:
521
- syntax1 += " [--%s=...]" % (arg,)
522
- else:
523
- syntax1 += " --%s=..." % (arg,)
512
+ for arg in argspec.kwonlyargs:
513
+ if argspec.kwonlydefaults and arg in argspec.kwonlydefaults:
514
+ syntax1 += " [--%s=...]" % (arg,)
515
+ else:
516
+ syntax1 += " --%s=..." % (arg,)
524
517
  if keywords:
525
518
  syntax1 += " [--...]"
526
519
  usage.append(syntax1)
@@ -531,12 +524,11 @@ def _build_function_usage_string(function_name, argspec, prefix):
531
524
  syntax2 += " [--%s=...]" % (arg,)
532
525
  else:
533
526
  syntax2 += " --%s=..." % (arg,)
534
- if sys.version_info[0] == 3:
535
- for arg in argspec.kwonlyargs:
536
- if argspec.kwonlydefaults and arg in argspec.kwonlydefaults:
537
- syntax2 += " [--%s=...]" % (arg,)
538
- else:
539
- syntax2 += " --%s=..." % (arg,)
527
+ for arg in argspec.kwonlyargs:
528
+ if argspec.kwonlydefaults and arg in argspec.kwonlydefaults:
529
+ syntax2 += " [--%s=...]" % (arg,)
530
+ else:
531
+ syntax2 += " --%s=..." % (arg,)
540
532
  if argspec.varargs:
541
533
  syntax2 += " %s..." % argspec.varargs
542
534
  if keywords:
@@ -717,20 +709,18 @@ def _parse_auto_apply_args(argspec, commandline_args, namespace, arg_mode="auto"
717
709
  for argname, default in zip(argspec.args[len(argspec.args)-len(defaults):],
718
710
  defaults):
719
711
  argname2default[argname] = make_expr(default, "raw_value")
720
- if sys.version_info[0] == 3:
721
- if argspec.kwonlydefaults:
722
- for argname, default in argspec.kwonlydefaults.items():
723
- argname2default[argname] = make_expr(default, "raw_value")
712
+ if argspec.kwonlydefaults:
713
+ for argname, default in argspec.kwonlydefaults.items():
714
+ argname2default[argname] = make_expr(default, "raw_value")
724
715
  # Create a map from prefix to arguments with that prefix. E.g. {"foo":
725
716
  # ["foobar", "foobaz"]}
726
717
  prefix2argname = {}
727
718
  for argname in argspec.args:
728
719
  for prefix in prefixes(argname):
729
720
  prefix2argname.setdefault(prefix, []).append(argname)
730
- if sys.version_info[0] == 3:
731
- for argname in argspec.kwonlyargs:
732
- for prefix in prefixes(argname):
733
- prefix2argname.setdefault(prefix, []).append(argname)
721
+ for argname in argspec.kwonlyargs:
722
+ for prefix in prefixes(argname):
723
+ prefix2argname.setdefault(prefix, []).append(argname)
734
724
  # Enumerate over input arguments.
735
725
  got_pos_args = []
736
726
  got_keyword_args = {}
@@ -769,14 +759,9 @@ def _parse_auto_apply_args(argspec, commandline_args, namespace, arg_mode="auto"
769
759
  raise _ParseInterruptedWantHelp
770
760
  if argname in ["source"]:
771
761
  raise _ParseInterruptedWantSource
772
- if sys.version_info[0] == 3:
773
- if not argspec.varkw:
774
- raise ParseError("Unknown option name %s" %
775
- (argname,))
776
- else:
777
- if not argspec.keywords:
778
- raise ParseError("Unknown option name %s" %
779
- (argname,))
762
+ if not argspec.varkw:
763
+ raise ParseError("Unknown option name %s" %
764
+ (argname,))
780
765
 
781
766
  elif len(matched_argnames) > 1:
782
767
  raise ParseError(
@@ -5,4 +5,4 @@
5
5
 
6
6
 
7
7
 
8
- __version__ = '1.8.6'
8
+ __version__ = '1.8.7'
@@ -27,10 +27,10 @@ import sys
27
27
  import os
28
28
 
29
29
  from pyflyby._cmdline import hfmt, parse_args, process_actions
30
- from pyflyby._imports2s import (canonicalize_imports,
31
- fix_unused_and_missing_imports,
32
- replace_star_imports,
33
- transform_imports)
30
+ from pyflyby._imports2s import (canonicalize_imports,
31
+ fix_unused_and_missing_imports,
32
+ replace_star_imports,
33
+ transform_imports, sort_imports)
34
34
  from pyflyby._log import logger
35
35
 
36
36
  import toml
@@ -145,20 +145,23 @@ def main():
145
145
  parser.set_defaults(**default_config)
146
146
  options, args = parse_args(
147
147
  _add_opts_and_defaults, import_format_params=True, modify_action_params=True, defaults=default_config)
148
+
148
149
  def modify(x):
149
- if options.canonicalize:
150
- x = canonicalize_imports(x, params=options.params)
151
150
  if options.transformations:
152
151
  x = transform_imports(x, options.transformations,
153
152
  params=options.params)
154
153
  if options.replace_star_imports:
155
154
  x = replace_star_imports(x, params=options.params)
156
- return fix_unused_and_missing_imports(
155
+ x = fix_unused_and_missing_imports(
157
156
  x, params=options.params,
158
157
  add_missing=options.add_missing,
159
158
  remove_unused=options.remove_unused,
160
159
  add_mandatory=options.add_mandatory,
161
160
  )
161
+ sorted_imports = sort_imports(x)
162
+ if options.canonicalize:
163
+ sorted_imports = canonicalize_imports(sorted_imports, params=options.params)
164
+ return sorted_imports
162
165
  process_actions(args, options.actions, modify)
163
166
 
164
167
 
@@ -609,17 +609,13 @@ class _MissingImportFinder(object):
609
609
  # scope.
610
610
  # - Store the name in the current scope (but not visibly to
611
611
  # args/decorator_list).
612
- if sys.version_info >= (3, 8):
613
- assert node._fields == ('name', 'args', 'body', 'decorator_list', 'returns', 'type_comment'), node._fields
614
- else:
615
- assert node._fields == ('name', 'args', 'body', 'decorator_list', 'returns'), node._fields
612
+ assert node._fields == ('name', 'args', 'body', 'decorator_list', 'returns', 'type_comment'), node._fields
616
613
  with self._NewScopeCtx(include_class_scopes=True):
617
614
  self.visit(node.args)
618
615
  self.visit(node.decorator_list)
619
616
  if node.returns:
620
617
  self.visit(node.returns)
621
- if sys.version_info >= (3, 8):
622
- self._visit_typecomment(node.type_comment)
618
+ self._visit_typecomment(node.type_comment)
623
619
  old_in_FunctionDef = self._in_FunctionDef
624
620
  self._in_FunctionDef = True
625
621
  with self._NewScopeCtx(unhide_classdef=True):
@@ -671,10 +667,7 @@ class _MissingImportFinder(object):
671
667
  self.visit(node)
672
668
 
673
669
  def visit_arguments(self, node):
674
- if sys.version_info >= (3, 8):
675
- assert node._fields == ('posonlyargs', 'args', 'vararg', 'kwonlyargs', 'kw_defaults', 'kwarg', 'defaults'), node._fields
676
- else:
677
- assert node._fields == ('args', 'vararg', 'kwonlyargs', 'kw_defaults', 'kwarg', 'defaults'), node._fields
670
+ assert node._fields == ('posonlyargs', 'args', 'vararg', 'kwonlyargs', 'kw_defaults', 'kwarg', 'defaults'), node._fields
678
671
  # Argument/parameter list. Note that the defaults should be
679
672
  # considered "Load"s from the upper scope, and the argument names are
680
673
  # "Store"s in the function scope.
@@ -693,8 +686,7 @@ class _MissingImportFinder(object):
693
686
  # Store arg names.
694
687
  self.visit(node.args)
695
688
  self.visit(node.kwonlyargs)
696
- if sys.version_info >= (3, 8):
697
- self.visit(node.posonlyargs)
689
+ self.visit(node.posonlyargs)
698
690
  # may be None.
699
691
  if node.vararg:
700
692
  self.visit(node.vararg)
@@ -807,16 +799,12 @@ class _MissingImportFinder(object):
807
799
  self._visit_fullname(node.id, node.ctx)
808
800
 
809
801
  def visit_arg(self, node):
810
- if sys.version_info >= (3, 8):
811
- assert node._fields == ('arg', 'annotation', 'type_comment'), node._fields
812
- else:
813
- assert node._fields == ('arg', 'annotation'), node._fields
802
+ assert node._fields == ('arg', 'annotation', 'type_comment'), node._fields
814
803
  if node.annotation:
815
804
  self.visit(node.annotation)
816
805
  # Treat it like a Name node would from Python 2
817
806
  self._visit_fullname(node.arg, ast.Param())
818
- if sys.version_info >= (3, 8):
819
- self._visit_typecomment(node.type_comment)
807
+ self._visit_typecomment(node.type_comment)
820
808
 
821
809
  def visit_Attribute(self, node):
822
810
  name_revparts = []
@@ -1512,8 +1500,7 @@ def find_missing_imports(arg, namespaces):
1512
1500
  else:
1513
1501
  return []
1514
1502
  # Parse the string into an AST.
1515
- kw = {} if sys.version_info < (3, 8) else {'type_comments': True}
1516
- node = ast.parse(arg, **kw) # may raise SyntaxError
1503
+ node = ast.parse(arg, type_comments=True) # may raise SyntaxError
1517
1504
  # Get missing imports from AST.
1518
1505
  return _find_missing_imports_in_ast(node, namespaces)
1519
1506
  elif isinstance(arg, PythonBlock):