pyflyby 1.8.5__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.
- {pyflyby-1.8.5/lib/python/pyflyby.egg-info → pyflyby-1.8.7}/PKG-INFO +2 -2
- {pyflyby-1.8.5 → pyflyby-1.8.7}/README.rst +1 -1
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/_autoimp.py +8 -21
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/_comms.py +81 -6
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/_idents.py +1 -1
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/_imports2s.py +44 -2
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/_modules.py +84 -24
- {pyflyby-1.8.5/lib/python → pyflyby-1.8.7/bin}/pyflyby/_parse.py +28 -109
- {pyflyby-1.8.5/lib/python → pyflyby-1.8.7/bin}/pyflyby/_py.py +22 -37
- {pyflyby-1.8.5/lib/python → pyflyby-1.8.7/bin}/pyflyby/_version.py +1 -1
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/tidy-imports +10 -7
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/_autoimp.py +8 -21
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/_comms.py +81 -6
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/_idents.py +1 -1
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/_imports2s.py +44 -2
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/_modules.py +84 -24
- {pyflyby-1.8.5/bin → pyflyby-1.8.7/lib/python}/pyflyby/_parse.py +28 -109
- {pyflyby-1.8.5/bin → pyflyby-1.8.7/lib/python}/pyflyby/_py.py +22 -37
- {pyflyby-1.8.5/bin → pyflyby-1.8.7/lib/python}/pyflyby/_version.py +1 -1
- {pyflyby-1.8.5 → pyflyby-1.8.7/lib/python/pyflyby.egg-info}/PKG-INFO +2 -2
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby.egg-info/requires.txt +1 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/setup.py +1 -1
- {pyflyby-1.8.5 → pyflyby-1.8.7}/tests/test_autoimp.py +2 -25
- {pyflyby-1.8.5 → pyflyby-1.8.7}/tests/test_cmdline.py +139 -2
- {pyflyby-1.8.5 → pyflyby-1.8.7}/tests/test_imports2s.py +11 -4
- {pyflyby-1.8.5 → pyflyby-1.8.7}/tests/test_interactive.py +9 -14
- {pyflyby-1.8.5 → pyflyby-1.8.7}/tests/test_jupyterlab_pyflyby.py +26 -1
- {pyflyby-1.8.5 → pyflyby-1.8.7}/tests/test_livepatch.py +1 -4
- {pyflyby-1.8.5 → pyflyby-1.8.7}/tests/test_parse.py +12 -24
- {pyflyby-1.8.5 → pyflyby-1.8.7}/tests/test_py.py +0 -22
- {pyflyby-1.8.5 → pyflyby-1.8.7}/.pyflyby +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/LICENSE.txt +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/MANIFEST.in +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/autoipython +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/autopython +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/collect-exports +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/collect-imports +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/create-imports +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/find-import +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/list-bad-xrefs +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/prune-broken-imports +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/__init__.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/__main__.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/_cmdline.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/_dbg.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/_docxref.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/_file.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/_flags.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/_format.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/_importclns.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/_importdb.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/_importstmt.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/_interactive.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/_livepatch.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/_log.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/_util.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/autoimport.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby/importdb.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/pyflyby-diff +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/reformat-imports +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/replace-star-imports +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/bin/transform-imports +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/LICENSE.txt +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/Makefile +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/TODO.txt +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/__init__.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/api/api.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/api/autoimp.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/api/cmdline.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/api/comms.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/api/dbg.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/api/file.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/api/flags.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/api/format.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/api/idents.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/api/importclns.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/api/importdb.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/api/imports2s.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/api/importstmt.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/api/interactive.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/api/livepatch.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/api/log.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/api/modules.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/api/parse.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/api/py.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/api/util.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/cli/autoipython.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/cli/cli.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/cli/collect_exports.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/cli/collect_imports.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/cli/find_import.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/cli/prune_broken_imports.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/cli/py.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/cli/pyflyby_diff.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/cli/reformat_imports.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/cli/replace_star_imports.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/cli/tidy_imports.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/cli/transform_imports.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/conf.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/index.rst +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/make.bat +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/doc/testing.txt +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/etc/pyflyby/canonical.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/etc/pyflyby/common.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/etc/pyflyby/forget.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/etc/pyflyby/mandatory.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/etc/pyflyby/numpy.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/etc/pyflyby/std.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/emacs/pyflyby.el +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/__init__.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/__main__.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/_cmdline.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/_dbg.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/_docxref.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/_file.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/_flags.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/_format.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/_importclns.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/_importdb.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/_importstmt.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/_interactive.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/_livepatch.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/_log.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/_util.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/autoimport.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby/importdb.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby.egg-info/SOURCES.txt +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby.egg-info/dependency_links.txt +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby.egg-info/entry_points.txt +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/lib/python/pyflyby.egg-info/top_level.txt +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/libexec/pyflyby/colordiff +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/libexec/pyflyby/diff-colorize +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/setup.cfg +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/tests/__init__.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/tests/test_0testconfig.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/tests/test_docxref.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/tests/test_file.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/tests/test_flags.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/tests/test_format.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/tests/test_idents.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/tests/test_importclns.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/tests/test_importdb.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/tests/test_importstmt.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/tests/test_modules.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/tests/test_util.py +0 -0
- {pyflyby-1.8.5 → pyflyby-1.8.7}/tests/xrefs.py +0 -0
- {pyflyby-1.8.5 → 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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 = []
|
|
@@ -1473,7 +1461,7 @@ def find_missing_imports(arg, namespaces):
|
|
|
1473
1461
|
['x']
|
|
1474
1462
|
|
|
1475
1463
|
>>> # Python 3
|
|
1476
|
-
>>> [str(m) for m in find_missing_imports("[x+y+z for x,y in [(1,2)]], y", [{}])]
|
|
1464
|
+
>>> [str(m) for m in find_missing_imports("[x+y+z for x,y in [(1,2)]], y", [{}])]
|
|
1477
1465
|
['y', 'z']
|
|
1478
1466
|
|
|
1479
1467
|
>>> [str(m) for m in find_missing_imports("(x+y+z for x,y in [(1,2)]), y", [{}])]
|
|
@@ -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
|
-
|
|
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):
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
|
|
2
|
-
from
|
|
3
|
-
|
|
2
|
+
from __future__ import print_function
|
|
3
|
+
|
|
4
|
+
from pyflyby._imports2s import (SourceToSourceFileImportsTransformation,
|
|
5
|
+
SourceToSourceImportBlockTransformation,
|
|
6
|
+
fix_unused_and_missing_imports,
|
|
7
|
+
replace_star_imports)
|
|
4
8
|
from pyflyby._importstmt import Import
|
|
9
|
+
from pyflyby._log import logger
|
|
5
10
|
import six
|
|
6
11
|
|
|
7
12
|
# These are comm targets that the frontend (lab/notebook) is expected to
|
|
@@ -11,10 +16,11 @@ import six
|
|
|
11
16
|
MISSING_IMPORTS = "pyflyby.missing_imports"
|
|
12
17
|
FORMATTING_IMPORTS = "pyflyby.format_imports"
|
|
13
18
|
INIT_COMMS = "pyflyby.init_comms"
|
|
19
|
+
TIDY_IMPORTS = "pyflyby.tidy_imports"
|
|
14
20
|
PYFLYBY_START_MSG = "# THIS CELL WAS AUTO-GENERATED BY PYFLYBY\n"
|
|
15
21
|
PYFLYBY_END_MSG = "# END AUTO-GENERATED BLOCK\n"
|
|
16
22
|
|
|
17
|
-
pyflyby_comm_targets= [MISSING_IMPORTS, FORMATTING_IMPORTS]
|
|
23
|
+
pyflyby_comm_targets= [MISSING_IMPORTS, FORMATTING_IMPORTS, TIDY_IMPORTS]
|
|
18
24
|
|
|
19
25
|
# A map of the comms opened with a given target name.
|
|
20
26
|
comms = {}
|
|
@@ -113,6 +119,41 @@ def _reformat_helper(input_code, imports):
|
|
|
113
119
|
|
|
114
120
|
return reformat_import_statements(before + bmarker + middle + emarker + after)
|
|
115
121
|
|
|
122
|
+
def extract_import_statements(text):
|
|
123
|
+
"""This is a util for notebook interactions and extracts import statements
|
|
124
|
+
from some python code. This function also re-orders imports.
|
|
125
|
+
Args:
|
|
126
|
+
code (str): The code from which import statements have to be extracted
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
(str, str): The first returned value contains all the import statements.
|
|
130
|
+
The second returned value is the remaining code after
|
|
131
|
+
extracting the import statements.
|
|
132
|
+
"""
|
|
133
|
+
transformer = SourceToSourceFileImportsTransformation(text)
|
|
134
|
+
imports = '\n'.join([str(im.pretty_print()) for im in transformer.import_blocks])
|
|
135
|
+
remaining_code = "\n".join([str(st.pretty_print()) if not isinstance(st, SourceToSourceImportBlockTransformation) else "" for st in transformer.blocks])
|
|
136
|
+
return imports, remaining_code
|
|
137
|
+
|
|
138
|
+
def collect_code_with_imports_on_top(imports: str, cell_array):
|
|
139
|
+
return (
|
|
140
|
+
imports
|
|
141
|
+
+ "\n"
|
|
142
|
+
+ "\n".join(
|
|
143
|
+
[
|
|
144
|
+
cell["text"] if cell["type"] == "code" else ""
|
|
145
|
+
for cell in cell_array
|
|
146
|
+
]
|
|
147
|
+
)
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def run_tidy_imports(code):
|
|
151
|
+
return str(
|
|
152
|
+
fix_unused_and_missing_imports(
|
|
153
|
+
replace_star_imports(code)
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
|
|
116
157
|
def comm_open_handler(comm, message):
|
|
117
158
|
"""
|
|
118
159
|
Handles comm_open message for pyflyby custom comm messages.
|
|
@@ -130,7 +171,41 @@ def comm_open_handler(comm, message):
|
|
|
130
171
|
def _recv(msg):
|
|
131
172
|
data = msg["content"]["data"]
|
|
132
173
|
if data["type"] == FORMATTING_IMPORTS:
|
|
133
|
-
msg_id = data.get(
|
|
134
|
-
imports = data.get(
|
|
174
|
+
msg_id = data.get("msg_id", None)
|
|
175
|
+
imports = data.get("imports", None)
|
|
135
176
|
fmt_code = _reformat_helper(data["input_code"], imports)
|
|
136
|
-
comm.send(
|
|
177
|
+
comm.send(
|
|
178
|
+
{
|
|
179
|
+
"msg_id": msg_id,
|
|
180
|
+
"formatted_code": str(fmt_code),
|
|
181
|
+
"type": FORMATTING_IMPORTS,
|
|
182
|
+
}
|
|
183
|
+
)
|
|
184
|
+
elif data["type"] == TIDY_IMPORTS:
|
|
185
|
+
checksum = data.get("checksum", '')
|
|
186
|
+
cell_array = data.get("cellArray", [])
|
|
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 = "", []
|
|
194
|
+
for cell in cell_array:
|
|
195
|
+
text = cell.get("text")
|
|
196
|
+
cell_type = cell.get("type")
|
|
197
|
+
if cell_type == "code":
|
|
198
|
+
imports, text = extract_import_statements(text)
|
|
199
|
+
import_statements += imports
|
|
200
|
+
processed_cell_array.append({"text": text, "type": cell_type})
|
|
201
|
+
code_with_collected_imports = collect_code_with_imports_on_top(import_statements, processed_cell_array)
|
|
202
|
+
code_post_tidy_imports = run_tidy_imports(code_with_collected_imports)
|
|
203
|
+
import_statements, _ = extract_import_statements(code_post_tidy_imports)
|
|
204
|
+
comm.send(
|
|
205
|
+
{
|
|
206
|
+
"checksum": checksum,
|
|
207
|
+
"type": TIDY_IMPORTS,
|
|
208
|
+
"cells": processed_cell_array,
|
|
209
|
+
"imports": import_statements,
|
|
210
|
+
}
|
|
211
|
+
)
|
|
@@ -194,7 +194,7 @@ class DottedIdentifier(object):
|
|
|
194
194
|
return (type(self)(x) for x in self.parts)
|
|
195
195
|
|
|
196
196
|
def __add__(self, suffix):
|
|
197
|
-
return type(self)("%s.%s"
|
|
197
|
+
return type(self)("%s.%s" % (self, suffix))
|
|
198
198
|
|
|
199
199
|
def __str__(self):
|
|
200
200
|
return self.name
|
|
@@ -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``.
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
|
|
8
|
+
import ast
|
|
7
9
|
from functools import total_ordering
|
|
10
|
+
import itertools
|
|
8
11
|
import os
|
|
9
12
|
import re
|
|
10
13
|
import six
|
|
@@ -67,13 +70,13 @@ def import_module(module_name):
|
|
|
67
70
|
real_importerror1, real_importerror2, real_importerror3)
|
|
68
71
|
if real_importerror1 and real_importerror2 and real_importerror3:
|
|
69
72
|
raise
|
|
70
|
-
reraise(ErrorDuringImportError(
|
|
73
|
+
reraise(ErrorDuringImportError, ErrorDuringImportError(
|
|
71
74
|
"Error while attempting to import %s: %s: %s"
|
|
72
|
-
% (module_name, type(e).__name__, e)),
|
|
75
|
+
% (module_name, type(e).__name__, e)), sys.exc_info()[2])
|
|
73
76
|
except Exception as e:
|
|
74
|
-
reraise(ErrorDuringImportError(
|
|
77
|
+
reraise(ErrorDuringImportError, ErrorDuringImportError(
|
|
75
78
|
"Error while attempting to import %s: %s: %s"
|
|
76
|
-
% (module_name, type(e).__name__, e)),
|
|
79
|
+
% (module_name, type(e).__name__, e)), sys.exc_info()[2])
|
|
77
80
|
|
|
78
81
|
|
|
79
82
|
def _my_iter_modules(path, prefix=''):
|
|
@@ -320,13 +323,26 @@ class ModuleHandle(object):
|
|
|
320
323
|
return tuple(ModuleHandle("%s.%s" % (self.name,m))
|
|
321
324
|
for m in sorted(set(submodule_names)))
|
|
322
325
|
|
|
326
|
+
@staticmethod
|
|
327
|
+
def _member_from_node(node):
|
|
328
|
+
extractors = {
|
|
329
|
+
# Top-level assignments (as opposed to member assignments
|
|
330
|
+
# whose targets are of type ast.Attribute).
|
|
331
|
+
ast.Assign: lambda x: [t.id for t in x.targets if isinstance(t, ast.Name)],
|
|
332
|
+
ast.ClassDef: lambda x: [x.name],
|
|
333
|
+
ast.FunctionDef: lambda x: [x.name],
|
|
334
|
+
}
|
|
335
|
+
if isinstance(node, tuple(extractors.keys())):
|
|
336
|
+
return extractors[type(node)](node)
|
|
337
|
+
return []
|
|
338
|
+
|
|
323
339
|
@cached_attribute
|
|
324
340
|
def exports(self):
|
|
325
341
|
"""
|
|
326
342
|
Get symbols exported by this module.
|
|
327
343
|
|
|
328
|
-
Note that this
|
|
329
|
-
|
|
344
|
+
Note that this will not recognize symbols that are dynamically
|
|
345
|
+
introduced to the module's namespace or __all__ list.
|
|
330
346
|
|
|
331
347
|
:rtype:
|
|
332
348
|
`ImportSet` or ``None``
|
|
@@ -334,28 +350,72 @@ class ModuleHandle(object):
|
|
|
334
350
|
Exports, or ``None`` if nothing exported.
|
|
335
351
|
"""
|
|
336
352
|
from pyflyby._importclns import ImportStatement, ImportSet
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
353
|
+
|
|
354
|
+
filename = getattr(self, 'filename', None)
|
|
355
|
+
if not filename or not filename.exists:
|
|
356
|
+
# Try to load the module to get the filename
|
|
357
|
+
filename = Filename(self.module.__file__)
|
|
358
|
+
text = FileText(filename)
|
|
359
|
+
|
|
360
|
+
ast_mod = ast.parse(str(text), str(filename)).body
|
|
361
|
+
|
|
362
|
+
# First, add members that are explicitly defined in the module
|
|
363
|
+
members = list(itertools.chain(*[self._member_from_node(n) \
|
|
364
|
+
for n in ast_mod]))
|
|
365
|
+
|
|
366
|
+
# If __all__ is defined, try to use it
|
|
367
|
+
all_is_good = False # pun intended
|
|
368
|
+
if "__all__" in members:
|
|
369
|
+
# Iterate through the nodes and reconstruct the
|
|
370
|
+
# value of __all__
|
|
371
|
+
for n in ast_mod:
|
|
372
|
+
if isinstance(n, ast.Assign):
|
|
373
|
+
if "__all__" in self._member_from_node(n):
|
|
374
|
+
try:
|
|
375
|
+
all_members = list(ast.literal_eval(n.value))
|
|
376
|
+
all_is_good = True
|
|
377
|
+
except (ValueError, TypeError):
|
|
378
|
+
all_is_good = False
|
|
379
|
+
elif isinstance(n, ast.AugAssign) and \
|
|
380
|
+
isinstance(n.target, ast.Name) and \
|
|
381
|
+
n.target.id == "__all__" and all_is_good:
|
|
382
|
+
try:
|
|
383
|
+
all_members += list(ast.literal_eval(n.value))
|
|
384
|
+
except (ValueError, TypeError):
|
|
385
|
+
all_is_good = False
|
|
355
386
|
if not all(type(s) == str for s in members):
|
|
356
387
|
raise Exception(
|
|
357
388
|
"Module %r contains non-string entries in __all__"
|
|
358
389
|
% (str(self.name),))
|
|
390
|
+
|
|
391
|
+
if all_is_good:
|
|
392
|
+
members = all_members
|
|
393
|
+
else:
|
|
394
|
+
# Add "from" imports that belong to submodules
|
|
395
|
+
# (note: this will fail to recognize implicit relative imports)
|
|
396
|
+
imp_nodes = [n for n in ast_mod if isinstance(n, ast.ImportFrom)]
|
|
397
|
+
for imp_node in imp_nodes:
|
|
398
|
+
if imp_node.level == 0:
|
|
399
|
+
from_mod = DottedIdentifier(imp_node.module)
|
|
400
|
+
if not from_mod.startswith(self.name):
|
|
401
|
+
continue
|
|
402
|
+
elif imp_node.level == 1 and \
|
|
403
|
+
filename.base == "__init__.py":
|
|
404
|
+
# Special case: a relative import can be from a submodule only if
|
|
405
|
+
# our module's filename is __init__.py.
|
|
406
|
+
from_mod = self.name
|
|
407
|
+
if imp_node.module:
|
|
408
|
+
from_mod += imp_node.module
|
|
409
|
+
else:
|
|
410
|
+
continue
|
|
411
|
+
for n in imp_node.names:
|
|
412
|
+
m = n.asname or n.name
|
|
413
|
+
if n.name != "*" and not ModuleHandle(from_mod + m).exists:
|
|
414
|
+
members.append(m)
|
|
415
|
+
|
|
416
|
+
# Filter by non-private.
|
|
417
|
+
members = [n for n in members if not n.startswith("_")]
|
|
418
|
+
|
|
359
419
|
# Filter out artificially added "deep" members.
|
|
360
420
|
members = [(n, None) for n in members if "." not in n]
|
|
361
421
|
if not members:
|
|
@@ -24,19 +24,7 @@ from pyflyby._util import cached_attribute, cmp
|
|
|
24
24
|
|
|
25
25
|
from ast import Bytes
|
|
26
26
|
|
|
27
|
-
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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]
|
|
@@ -153,7 +131,7 @@ def _iter_child_nodes_in_order_internal_1(node):
|
|
|
153
131
|
assert node._fields == ('name', 'bases', 'keywords', 'body', 'decorator_list')
|
|
154
132
|
yield node.decorator_list, node.bases, node.body
|
|
155
133
|
# node.name is a string, not an AST node
|
|
156
|
-
elif
|
|
134
|
+
elif isinstance(node, ast.FormattedValue):
|
|
157
135
|
assert node._fields == ('value', 'conversion', 'format_spec')
|
|
158
136
|
yield node.value,
|
|
159
137
|
else:
|
|
@@ -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
|
|
191
|
-
|
|
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
|
-
|
|
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 (
|
|
404
|
-
|
|
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)
|
|
@@ -426,20 +383,6 @@ def _annotate_ast_startpos(ast_node, parent_ast_node, minpos, text, flags):
|
|
|
426
383
|
# Since we use startpos for breaking lines, we need to set startpos to
|
|
427
384
|
# the beginning of the line.
|
|
428
385
|
# In Python 3, the col_offset for the with is 0 again.
|
|
429
|
-
if (isinstance(ast_node, ast.With) and
|
|
430
|
-
not isinstance(parent_ast_node, ast.With) and
|
|
431
|
-
sys.version_info[:2] == (2,7)):
|
|
432
|
-
assert ast_node.col_offset >= 5
|
|
433
|
-
if startpos.lineno == text.startpos.lineno:
|
|
434
|
-
linestart = text.startpos.colno
|
|
435
|
-
else:
|
|
436
|
-
linestart = 1
|
|
437
|
-
line = text[(startpos.lineno,linestart):startpos]
|
|
438
|
-
m = re.search(r"\bwith\s+$", str(line))
|
|
439
|
-
assert m
|
|
440
|
-
lk = len(m.group()) # length of 'with ' including spaces
|
|
441
|
-
startpos = FilePos(startpos.lineno, startpos.colno - lk)
|
|
442
|
-
assert str(text[startpos:(startpos+(0,4))]) == "with"
|
|
443
386
|
ast_node.startpos = startpos
|
|
444
387
|
if sys.version_info <= (3, 8):
|
|
445
388
|
ast_node.startpos = max(startpos, minpos)
|
|
@@ -472,7 +415,7 @@ def _annotate_ast_startpos(ast_node, parent_ast_node, minpos, text, flags):
|
|
|
472
415
|
|
|
473
416
|
# It should now be the case that we are looking at a multi-line string
|
|
474
417
|
# literal.
|
|
475
|
-
if
|
|
418
|
+
if isinstance(ast_node, ast.FormattedValue):
|
|
476
419
|
ast_node.startpos = ast_node.value.startpos
|
|
477
420
|
ast_node.endpos = ast_node.value.startpos
|
|
478
421
|
|
|
@@ -500,9 +443,6 @@ def _annotate_ast_startpos(ast_node, parent_ast_node, minpos, text, flags):
|
|
|
500
443
|
for _m in re.finditer("[bBrRuU]*[\"\']", start_line)])
|
|
501
444
|
target_str = ast_node.s
|
|
502
445
|
|
|
503
|
-
if isinstance(target_str, bytes) and sys.version_info[:2] == (3, 7):
|
|
504
|
-
target_str = target_str.decode()
|
|
505
|
-
|
|
506
446
|
# Loop over possible end_linenos. The first one we've identified is the
|
|
507
447
|
# by far most likely one, but in theory it could be anywhere later in the
|
|
508
448
|
# file. This could be because of a dastardly concatenated string like
|
|
@@ -567,18 +507,8 @@ def _annotate_ast_startpos(ast_node, parent_ast_node, minpos, text, flags):
|
|
|
567
507
|
candidate_str = _test_parse_string_literal(subtext, flags)
|
|
568
508
|
if candidate_str is None:
|
|
569
509
|
continue
|
|
570
|
-
if isinstance(candidate_str, bytes) and sys.version_info[:2] == (3, 7):
|
|
571
|
-
candidate_str = candidate_str.decode()
|
|
572
510
|
|
|
573
511
|
maybe_fstring = False
|
|
574
|
-
try:
|
|
575
|
-
if (3, 7) <= sys.version_info <= (3, 8):
|
|
576
|
-
potential_start = text.lines[startpos.lineno - 1]
|
|
577
|
-
maybe_fstring = ("f'" in potential_start) or (
|
|
578
|
-
'f"' in potential_start
|
|
579
|
-
)
|
|
580
|
-
except IndexError:
|
|
581
|
-
pass
|
|
582
512
|
|
|
583
513
|
if target_str == candidate_str and target_str:
|
|
584
514
|
# Success!
|
|
@@ -603,17 +533,6 @@ def _annotate_ast_startpos(ast_node, parent_ast_node, minpos, text, flags):
|
|
|
603
533
|
for (sq, sp) in startpos_candidates
|
|
604
534
|
if sp in matched_prefix
|
|
605
535
|
]
|
|
606
|
-
if (3, 7) <= sys.version_info <= (3, 8):
|
|
607
|
-
if len(f_string_candidate_prefixes) == 1:
|
|
608
|
-
# we did not find the string but there is one fstring candidate starting it
|
|
609
|
-
|
|
610
|
-
ast_node.startpos, ast_node.endpos = f_string_candidate_prefixes[0]
|
|
611
|
-
return True
|
|
612
|
-
elif isinstance(parent_ast_node, ast.JoinedStr):
|
|
613
|
-
self_pos = parent_ast_node.values.index(ast_node)
|
|
614
|
-
ast_node.startpos = parent_ast_node.values[self_pos - 1].startpos
|
|
615
|
-
ast_node.endpos = parent_ast_node.values[self_pos - 1].endpos
|
|
616
|
-
return True
|
|
617
536
|
raise ValueError("Couldn't find exact position of %s" % (ast.dump(ast_node)))
|
|
618
537
|
|
|
619
538
|
|
|
@@ -801,7 +720,7 @@ class PythonStatement(object):
|
|
|
801
720
|
comments/blank lines.
|
|
802
721
|
|
|
803
722
|
>>> PythonStatement('print("x",\n file=None)\n', flags='print_function') #doctest: +SKIP
|
|
804
|
-
PythonStatement('print("x",\n file=None)\n', flags=
|
|
723
|
+
PythonStatement('print("x",\n file=None)\n', flags=0x100000)
|
|
805
724
|
|
|
806
725
|
Implemented as a wrapper around a `PythonBlock` containing at most one
|
|
807
726
|
top-level AST node.
|