diff --git a/src/python_minifier/transforms/combine_imports.py b/src/python_minifier/transforms/combine_imports.py index 5373b2ed..43384ec5 100644 --- a/src/python_minifier/transforms/combine_imports.py +++ b/src/python_minifier/transforms/combine_imports.py @@ -14,19 +14,22 @@ class CombineImports(SuiteTransformer): def _combine_import(self, node_list, parent): alias = [] + namespace = None for statement in node_list: + namespace = statement.namespace if isinstance(statement, ast.Import): alias += statement.names else: if alias: - yield self.add_child(ast.Import(names=alias), parent=parent) + yield self.add_child(ast.Import(names=alias), parent=parent, namespace=namespace) alias = [] yield statement if alias: - yield self.add_child(ast.Import(names=alias), parent=parent) + yield self.add_child(ast.Import(names=alias), parent=parent, namespace=namespace) + def _combine_import_from(self, node_list, parent): @@ -55,7 +58,7 @@ def combine(statement): else: if alias: yield self.add_child( - ast.ImportFrom(module=prev_import.module, names=alias, level=prev_import.level), parent=parent + ast.ImportFrom(module=prev_import.module, names=alias, level=prev_import.level), parent=parent, namespace=prev_import.namespace ) alias = [] @@ -63,7 +66,7 @@ def combine(statement): if alias: yield self.add_child( - ast.ImportFrom(module=prev_import.module, names=alias, level=prev_import.level), parent=parent + ast.ImportFrom(module=prev_import.module, names=alias, level=prev_import.level), parent=parent, namespace=prev_import.namespace ) def suite(self, node_list, parent): diff --git a/test/helpers.py b/test/helpers.py new file mode 100644 index 00000000..44efa8d2 --- /dev/null +++ b/test/helpers.py @@ -0,0 +1,50 @@ +import ast + +from python_minifier.rename import add_namespace, resolve_names +from python_minifier.rename.bind_names import bind_names +from python_minifier.rename.util import iter_child_namespaces +from python_minifier.util import is_ast_node + + +def assert_namespace_tree(source, expected_tree): + tree = ast.parse(source) + + add_namespace(tree) + bind_names(tree) + resolve_names(tree) + + actual = print_namespace(tree) + + print(actual) + assert actual.strip() == expected_tree.strip() + + +def print_namespace(namespace, indent=''): + s = '' + + if not indent: + s += '\n' + + def namespace_name(node): + if is_ast_node(node, (ast.FunctionDef, 'AsyncFunctionDef')): + return 'Function ' + node.name + elif isinstance(node, ast.ClassDef): + return 'Class ' + node.name + else: + return namespace.__class__.__name__ + + s += indent + '+ ' + namespace_name(namespace) + '\n' + + for name in sorted(namespace.global_names): + s += indent + ' - global ' + name + '\n' + + for name in sorted(namespace.nonlocal_names): + s += indent + ' - nonlocal ' + name + '\n' + + for binding in sorted(namespace.bindings, key=lambda b: b.name): + s += indent + ' - ' + repr(binding) + '\n' + + for child in iter_child_namespaces(namespace): + s += print_namespace(child, indent=indent + ' ') + + return s diff --git a/test/test_bind_names.py b/test/test_bind_names.py index a3813eb7..e5c4c1e3 100644 --- a/test/test_bind_names.py +++ b/test/test_bind_names.py @@ -1,57 +1,10 @@ -import ast import sys import pytest -from python_minifier.rename import add_namespace, resolve_names -from python_minifier.rename.bind_names import bind_names -from python_minifier.rename.util import iter_child_namespaces -from python_minifier.util import is_ast_node +from helpers import assert_namespace_tree -def assert_namespace_tree(source, expected_tree): - tree = ast.parse(source) - - add_namespace(tree) - bind_names(tree) - resolve_names(tree) - - actual = print_namespace(tree) - - print(actual) - assert actual.strip() == expected_tree.strip() - - -def print_namespace(namespace, indent=''): - s = '' - - if not indent: - s += '\n' - - def namespace_name(node): - if is_ast_node(node, (ast.FunctionDef, 'AsyncFunctionDef')): - return 'Function ' + node.name - elif isinstance(node, ast.ClassDef): - return 'Class ' + node.name - else: - return namespace.__class__.__name__ - - s += indent + '+ ' + namespace_name(namespace) + '\n' - - for name in sorted(namespace.global_names): - s += indent + ' - global ' + name + '\n' - - for name in sorted(namespace.nonlocal_names): - s += indent + ' - nonlocal ' + name + '\n' - - for binding in namespace.bindings: - s += indent + ' - ' + repr(binding) + '\n' - - for child in iter_child_namespaces(namespace): - s += print_namespace(child, indent=indent + ' ') - - return s - def test_module_namespace(): if sys.version_info < (3, 4): pytest.skip('Test requires python 3.4 or later') @@ -80,8 +33,8 @@ def test_lambda_namespace(): expected_namespaces = ''' + Module - - NameBinding(name='name_in_module', allow_rename=True) - NameBinding(name='b', allow_rename=True) + - NameBinding(name='name_in_module', allow_rename=True) + Lambda - NameBinding(name='arg', allow_rename=False) - NameBinding(name='args', allow_rename=True) @@ -108,15 +61,15 @@ def inner_func(): expected_namespaces = ''' + Module - - NameBinding(name='name_in_module', allow_rename=True) - NameBinding(name='func', allow_rename=True) + - NameBinding(name='name_in_module', allow_rename=True) - BuiltinBinding(name='print', allow_rename=True) + Function func - NameBinding(name='arg', allow_rename=True) - NameBinding(name='args', allow_rename=True) + - NameBinding(name='inner_func', allow_rename=True) - NameBinding(name='kwargs', allow_rename=True) - NameBinding(name='name_in_func', allow_rename=True) - - NameBinding(name='inner_func', allow_rename=True) + Function inner_func - NameBinding(name='name_in_inner_func', allow_rename=True) ''' @@ -141,15 +94,15 @@ async def inner_func(): expected_namespaces = ''' + Module - - NameBinding(name='name_in_module', allow_rename=True) - NameBinding(name='func', allow_rename=True) + - NameBinding(name='name_in_module', allow_rename=True) - BuiltinBinding(name='print', allow_rename=True) + Function func - NameBinding(name='arg', allow_rename=True) - NameBinding(name='args', allow_rename=True) + - NameBinding(name='inner_func', allow_rename=True) - NameBinding(name='kwargs', allow_rename=True) - NameBinding(name='name_in_func', allow_rename=True) - - NameBinding(name='inner_func', allow_rename=True) + Function inner_func - NameBinding(name='name_in_inner_func', allow_rename=True) ''' @@ -188,9 +141,9 @@ def test_multi_generator_namespace(): expected_namespaces = ''' + Module - - NameBinding(name='x', allow_rename=True) - - NameBinding(name='f', allow_rename=True) - NameBinding(name='a', allow_rename=True) + - NameBinding(name='f', allow_rename=True) + - NameBinding(name='x', allow_rename=True) + GeneratorExp - NameBinding(name='x', allow_rename=True) ''' @@ -210,13 +163,13 @@ def test_multi_generator_namespace_2(): expected_namespaces = ''' + Module + - NameBinding(name='a', allow_rename=True) - NameBinding(name='c', allow_rename=True) - - NameBinding(name='line', allow_rename=True) - NameBinding(name='file', allow_rename=True) - - NameBinding(name='a', allow_rename=True) + - NameBinding(name='line', allow_rename=True) + GeneratorExp - - NameBinding(name='line', allow_rename=True) - NameBinding(name='c', allow_rename=True) + - NameBinding(name='line', allow_rename=True) ''' assert_namespace_tree(source, expected_namespaces) @@ -234,10 +187,10 @@ def test_nested_generator(): expected_namespaces = ''' + Module + - NameBinding(name='a', allow_rename=True) - NameBinding(name='c', allow_rename=True) - - NameBinding(name='line', allow_rename=True) - NameBinding(name='file', allow_rename=True) - - NameBinding(name='a', allow_rename=True) + - NameBinding(name='line', allow_rename=True) + GeneratorExp - NameBinding(name='c', allow_rename=True) + GeneratorExp @@ -257,8 +210,8 @@ def test_nested_generator_2(): expected_namespaces = ''' + Module - - NameBinding(name='x', allow_rename=True) - NameBinding(name='a', allow_rename=True) + - NameBinding(name='x', allow_rename=True) + GeneratorExp - NameBinding(name='x', allow_rename=True) + GeneratorExp @@ -302,9 +255,9 @@ def test_multi_setcomp_namespace(): expected_namespaces = ''' + Module - - NameBinding(name='x', allow_rename=True) - - NameBinding(name='f', allow_rename=True) - NameBinding(name='a', allow_rename=True) + - NameBinding(name='f', allow_rename=True) + - NameBinding(name='x', allow_rename=True) + SetComp - NameBinding(name='x', allow_rename=True) ''' @@ -324,13 +277,13 @@ def test_multi_setcomp_namespace_2(): expected_namespaces = ''' + Module + - NameBinding(name='a', allow_rename=True) - NameBinding(name='c', allow_rename=True) - - NameBinding(name='line', allow_rename=True) - NameBinding(name='file', allow_rename=True) - - NameBinding(name='a', allow_rename=True) + - NameBinding(name='line', allow_rename=True) + SetComp - - NameBinding(name='line', allow_rename=True) - NameBinding(name='c', allow_rename=True) + - NameBinding(name='line', allow_rename=True) ''' assert_namespace_tree(source, expected_namespaces) @@ -348,10 +301,10 @@ def test_nested_setcomp(): expected_namespaces = ''' + Module + - NameBinding(name='a', allow_rename=True) - NameBinding(name='c', allow_rename=True) - - NameBinding(name='line', allow_rename=True) - NameBinding(name='file', allow_rename=True) - - NameBinding(name='a', allow_rename=True) + - NameBinding(name='line', allow_rename=True) + SetComp - NameBinding(name='c', allow_rename=True) + SetComp @@ -371,8 +324,8 @@ def test_nested_setcomp_2(): expected_namespaces = ''' + Module - - NameBinding(name='x', allow_rename=True) - NameBinding(name='a', allow_rename=True) + - NameBinding(name='x', allow_rename=True) + SetComp - NameBinding(name='x', allow_rename=True) + SetComp @@ -415,9 +368,9 @@ def test_multi_listcomp_namespace(): expected_namespaces = ''' + Module - - NameBinding(name='x', allow_rename=True) - - NameBinding(name='f', allow_rename=True) - NameBinding(name='a', allow_rename=True) + - NameBinding(name='f', allow_rename=True) + - NameBinding(name='x', allow_rename=True) + ListComp - NameBinding(name='x', allow_rename=True) ''' @@ -437,13 +390,13 @@ def test_multi_listcomp_namespace_2(): expected_namespaces = ''' + Module + - NameBinding(name='a', allow_rename=True) - NameBinding(name='c', allow_rename=True) - - NameBinding(name='line', allow_rename=True) - NameBinding(name='file', allow_rename=True) - - NameBinding(name='a', allow_rename=True) + - NameBinding(name='line', allow_rename=True) + ListComp - - NameBinding(name='line', allow_rename=True) - NameBinding(name='c', allow_rename=True) + - NameBinding(name='line', allow_rename=True) ''' assert_namespace_tree(source, expected_namespaces) @@ -461,10 +414,10 @@ def test_nested_listcomp(): expected_namespaces = ''' + Module + - NameBinding(name='a', allow_rename=True) - NameBinding(name='c', allow_rename=True) - - NameBinding(name='line', allow_rename=True) - NameBinding(name='file', allow_rename=True) - - NameBinding(name='a', allow_rename=True) + - NameBinding(name='line', allow_rename=True) + ListComp - NameBinding(name='c', allow_rename=True) + ListComp @@ -484,8 +437,8 @@ def test_nested_listcomp_2(): expected_namespaces = ''' + Module - - NameBinding(name='x', allow_rename=True) - NameBinding(name='a', allow_rename=True) + - NameBinding(name='x', allow_rename=True) + ListComp - NameBinding(name='x', allow_rename=True) + ListComp @@ -528,9 +481,9 @@ def test_multi_dictcomp_namespace(): expected_namespaces = ''' + Module - - NameBinding(name='x', allow_rename=True) - - NameBinding(name='f', allow_rename=True) - NameBinding(name='a', allow_rename=True) + - NameBinding(name='f', allow_rename=True) + - NameBinding(name='x', allow_rename=True) + DictComp - NameBinding(name='x', allow_rename=True) ''' @@ -550,13 +503,13 @@ def test_multi_dictcomp_namespace_2(): expected_namespaces = ''' + Module + - NameBinding(name='a', allow_rename=True) - NameBinding(name='c', allow_rename=True) - - NameBinding(name='line', allow_rename=True) - NameBinding(name='file', allow_rename=True) - - NameBinding(name='a', allow_rename=True) + - NameBinding(name='line', allow_rename=True) + DictComp - - NameBinding(name='line', allow_rename=True) - NameBinding(name='c', allow_rename=True) + - NameBinding(name='line', allow_rename=True) ''' assert_namespace_tree(source, expected_namespaces) @@ -574,10 +527,10 @@ def test_nested_dictcomp(): expected_namespaces = ''' + Module + - NameBinding(name='a', allow_rename=True) - NameBinding(name='c', allow_rename=True) - - NameBinding(name='line', allow_rename=True) - NameBinding(name='file', allow_rename=True) - - NameBinding(name='a', allow_rename=True) + - NameBinding(name='line', allow_rename=True) + DictComp - NameBinding(name='c', allow_rename=True) + DictComp @@ -597,8 +550,8 @@ def test_nested_dictcomp_2(): expected_namespaces = ''' + Module - - NameBinding(name='x', allow_rename=True) - NameBinding(name='a', allow_rename=True) + - NameBinding(name='x', allow_rename=True) + DictComp - NameBinding(name='x', allow_rename=True) + DictComp @@ -629,8 +582,8 @@ class C: expected_namespaces = ''' + Module - - NameBinding(name='OhALongName', allow_rename=False) - NameBinding(name='MyOtherName', allow_rename=True) + - NameBinding(name='OhALongName', allow_rename=False) - NameBinding(name='func', allow_rename=True) - BuiltinBinding(name='int', allow_rename=True) + Function func @@ -638,8 +591,8 @@ class C: + Class C - nonlocal OhALongName - nonlocal int - - NameBinding(name='MyOtherName', allow_rename=False) - NameBinding(name='ClassName', allow_rename=False) + - NameBinding(name='MyOtherName', allow_rename=False) ''' assert_namespace_tree(source, expected_namespaces) @@ -665,8 +618,8 @@ class C: expected_namespaces = ''' + Module - - NameBinding(name='OhALongName', allow_rename=False) - NameBinding(name='MyOtherName', allow_rename=True) + - NameBinding(name='OhALongName', allow_rename=False) - NameBinding(name='func', allow_rename=True) + Function func - NameBinding(name='C', allow_rename=True) diff --git a/test/test_bind_names_python2.py b/test/test_bind_names_python2.py index 03f412d4..3c255316 100644 --- a/test/test_bind_names_python2.py +++ b/test/test_bind_names_python2.py @@ -1,57 +1,8 @@ -import ast import sys import pytest -from python_minifier.rename import add_namespace, resolve_names -from python_minifier.rename.bind_names import bind_names -from python_minifier.rename.util import iter_child_namespaces -from python_minifier.util import is_ast_node - - -def assert_namespace_tree(source, expected_tree): - tree = ast.parse(source) - - add_namespace(tree) - bind_names(tree) - resolve_names(tree) - - actual = print_namespace(tree) - - print(actual) - assert actual.strip() == expected_tree.strip() - - -def print_namespace(namespace, indent=''): - s = '' - - if not indent: - s += '\n' - - def namespace_name(node): - if is_ast_node(node, (ast.FunctionDef, 'AsyncFunctionDef')): - return 'Function ' + node.name - elif isinstance(node, ast.ClassDef): - return 'Class ' + node.name - else: - return namespace.__class__.__name__ - - s += indent + '+ ' + namespace_name(namespace) + '\n' - - for name in sorted(namespace.global_names): - s += indent + ' - global ' + name + '\n' - - for name in sorted(namespace.nonlocal_names): - s += indent + ' - nonlocal ' + name + '\n' - - for binding in sorted(namespace.bindings, key=lambda b: b.name): - s += indent + ' - ' + repr(binding) + '\n' - - for child in iter_child_namespaces(namespace): - s += print_namespace(child, indent=indent + ' ') - - return s - +from helpers import assert_namespace_tree def test_module_namespace(): if sys.version_info >= (3, 0): diff --git a/test/test_bind_names_python33.py b/test/test_bind_names_python33.py index d527d922..d0ab6b2b 100644 --- a/test/test_bind_names_python33.py +++ b/test/test_bind_names_python33.py @@ -1,57 +1,8 @@ -import ast import sys import pytest -from python_minifier.rename import add_namespace, resolve_names -from python_minifier.rename.bind_names import bind_names -from python_minifier.rename.util import iter_child_namespaces -from python_minifier.util import is_ast_node - - -def assert_namespace_tree(source, expected_tree): - tree = ast.parse(source) - - add_namespace(tree) - bind_names(tree) - resolve_names(tree) - - actual = print_namespace(tree) - - print(actual) - assert actual.strip() == expected_tree.strip() - - -def print_namespace(namespace, indent=''): - s = '' - - if not indent: - s += '\n' - - def namespace_name(node): - if is_ast_node(node, (ast.FunctionDef, 'AsyncFunctionDef')): - return 'Function ' + node.name - elif isinstance(node, ast.ClassDef): - return 'Class ' + node.name - else: - return namespace.__class__.__name__ - - s += indent + '+ ' + namespace_name(namespace) + '\n' - - for name in sorted(namespace.global_names): - s += indent + ' - global ' + name + '\n' - - for name in sorted(namespace.nonlocal_names): - s += indent + ' - nonlocal ' + name + '\n' - - for binding in sorted(namespace.bindings, key=lambda b: b.name): - s += indent + ' - ' + repr(binding) + '\n' - - for child in iter_child_namespaces(namespace): - s += print_namespace(child, indent=indent + ' ') - - return s - +from helpers import assert_namespace_tree def test_module_namespace(): if sys.version_info < (3, 3) or sys.version_info > (3, 4): diff --git a/test/test_combine_imports.py b/test/test_combine_imports.py index 37dfe26a..ec8933b9 100644 --- a/test/test_combine_imports.py +++ b/test/test_combine_imports.py @@ -1,6 +1,8 @@ import ast +from helpers import print_namespace from python_minifier import add_namespace +from python_minifier.rename import bind_names, resolve_names from python_minifier.transforms.combine_imports import CombineImports from python_minifier.ast_compare import compare_ast @@ -9,6 +11,19 @@ def combine_imports(module): CombineImports()(module) return module +def assert_namespace_tree(source, expected_tree): + tree = ast.parse(source) + + add_namespace(tree) + CombineImports()(tree) + bind_names(tree) + resolve_names(tree) + + actual = print_namespace(tree) + + print(actual) + assert actual.strip() == expected_tree.strip() + def test_import(): source = '''import builtins import collections''' @@ -81,3 +96,68 @@ def test_import_star(): expected_ast = ast.parse(expected) actual_ast = combine_imports(ast.parse(source)) compare_ast(expected_ast, actual_ast) + +def test_import_as_class_namespace(): + + source = ''' +class MyClass: + import os as Hello +''' + + expected_namespaces = ''' ++ Module + - NameBinding(name='MyClass', allow_rename=True) + + Class MyClass + - NameBinding(name='Hello', allow_rename=False) +''' + + assert_namespace_tree(source, expected_namespaces) + +def test_import_class_namespace(): + + source = ''' +class MyClass: + import Hello +''' + + expected_namespaces = ''' ++ Module + - NameBinding(name='MyClass', allow_rename=True) + + Class MyClass + - NameBinding(name='Hello', allow_rename=False) +''' + + assert_namespace_tree(source, expected_namespaces) + +def test_from_import_class_namespace(): + + source = ''' +class MyClass: + from hello import Hello +''' + + expected_namespaces = ''' ++ Module + - NameBinding(name='MyClass', allow_rename=True) + + Class MyClass + - NameBinding(name='Hello', allow_rename=False) +''' + + assert_namespace_tree(source, expected_namespaces) + +def test_from_import_as_class_namespace(): + + source = ''' +class MyClass: + from hello import babadook as Hello +''' + + expected_namespaces = ''' ++ Module + - NameBinding(name='MyClass', allow_rename=True) + + Class MyClass + - NameBinding(name='Hello', allow_rename=False) +''' + + assert_namespace_tree(source, expected_namespaces) +