python-minifier
Advanced tools
| """ | ||
| This module provides utilities for annotating Abstract Syntax Tree (AST) nodes with parent references. | ||
| """ | ||
| import ast | ||
| class _NoParent(ast.AST): | ||
| """A placeholder class used to indicate that a node has no parent.""" | ||
| def __repr__(self): | ||
| # type: () -> str | ||
| return 'NoParent()' | ||
| def add_parent(node, parent=_NoParent()): | ||
| # type: (ast.AST, ast.AST) -> None | ||
| """ | ||
| Recursively adds a parent reference to each node in the AST. | ||
| >>> tree = ast.parse('a = 1') | ||
| >>> add_parent(tree) | ||
| >>> get_parent(tree.body[0]) == tree | ||
| True | ||
| :param node: The current AST node. | ||
| :param parent: The parent :class:`ast.AST` node. | ||
| """ | ||
| node._parent = parent # type: ignore[attr-defined] | ||
| for child in ast.iter_child_nodes(node): | ||
| add_parent(child, node) | ||
| def get_parent(node): | ||
| # type: (ast.AST) -> ast.AST | ||
| """ | ||
| Retrieves the parent of the given AST node. | ||
| >>> tree = ast.parse('a = 1') | ||
| >>> add_parent(tree) | ||
| >>> get_parent(tree.body[0]) == tree | ||
| True | ||
| :param node: The AST node whose parent is to be retrieved. | ||
| :return: The parent AST node. | ||
| :raises ValueError: If the node has no parent. | ||
| """ | ||
| if not hasattr(node, '_parent') or isinstance(node._parent, _NoParent): # type: ignore[attr-defined] | ||
| raise ValueError('Node has no parent') | ||
| return node._parent # type: ignore[attr-defined] | ||
| def set_parent(node, parent): | ||
| # type: (ast.AST, ast.AST) -> None | ||
| """ | ||
| Replace the parent of the given AST node. | ||
| Create a simple AST: | ||
| >>> tree = ast.parse('a = func()') | ||
| >>> add_parent(tree) | ||
| >>> isinstance(tree.body[0], ast.Assign) and isinstance(tree.body[0].value, ast.Call) | ||
| True | ||
| >>> assign = tree.body[0] | ||
| >>> call = tree.body[0].value | ||
| >>> get_parent(call) == assign | ||
| True | ||
| Replace the parent of the call node: | ||
| >>> tree.body[0] = call | ||
| >>> set_parent(call, tree) | ||
| >>> get_parent(call) == tree | ||
| True | ||
| >>> from python_minifier.ast_printer import print_ast | ||
| >>> print(print_ast(tree)) | ||
| Module(body=[ | ||
| Call(Name('func')) | ||
| ]) | ||
| :param node: The AST node whose parent is to be set. | ||
| :param parent: The parent AST node. | ||
| """ | ||
| node._parent = parent # type: ignore[attr-defined] |
| import sys | ||
| import pytest | ||
| from helpers import assert_namespace_tree | ||
| def test_namedexpr_in_module(): | ||
| if sys.version_info < (3, 8): | ||
| pytest.skip('Test is for >= python3.8 only') | ||
| source = ''' | ||
| (a := 1) | ||
| ''' | ||
| expected_namespaces = ''' | ||
| + Module | ||
| - NameBinding(name='a', allow_rename=True) <references=1> | ||
| ''' | ||
| assert_namespace_tree(source, expected_namespaces) | ||
| def test_namedexpr_in_function(): | ||
| if sys.version_info < (3, 8): | ||
| pytest.skip('Test is for >= python3.8 only') | ||
| source = ''' | ||
| def test(): | ||
| (a := 1) | ||
| lambda x: (x := 1) | ||
| ''' | ||
| expected_namespaces = ''' | ||
| + Module | ||
| - NameBinding(name='test', allow_rename=True) <references=1> | ||
| + Function test | ||
| - NameBinding(name='a', allow_rename=True) <references=1> | ||
| + Lambda | ||
| - NameBinding(name='x', allow_rename=False) <references=2> | ||
| ''' | ||
| assert_namespace_tree(source, expected_namespaces) | ||
| def test_namedexpr_in_listcomp_if_nonlocal(): | ||
| if sys.version_info < (3, 8): | ||
| pytest.skip('Test is for >= python3.8 only') | ||
| source = ''' | ||
| def f(arg, /): | ||
| nonlocal x | ||
| print([x for y in range(10) if (x := y // 2) & 1]) | ||
| print(arg, arg) | ||
| ''' | ||
| expected_namespaces = ''' | ||
| + Module | ||
| - NameBinding(name='f', allow_rename=True) <references=1> | ||
| - BuiltinBinding(name='print', allow_rename=True) <references=2> | ||
| - BuiltinBinding(name='range', allow_rename=True) <references=1> | ||
| - NameBinding(name='x', allow_rename=False) <references=3> | ||
| + Function f | ||
| - nonlocal x | ||
| - NameBinding(name='arg', allow_rename=True) <references=3> | ||
| + ListComp | ||
| - NameBinding(name='y', allow_rename=True) <references=2> | ||
| ''' | ||
| assert_namespace_tree(source, expected_namespaces) | ||
| def test_namedexpr_in_listcomp_if_global(): | ||
| if sys.version_info < (3, 8): | ||
| pytest.skip('Test is for >= python3.8 only') | ||
| source = ''' | ||
| def f2(): | ||
| def f(arg, /): | ||
| global x | ||
| print([x for y in range(10) if (x := y // 2) & 1]) | ||
| print(arg, arg) | ||
| ''' | ||
| expected_namespaces = ''' | ||
| + Module | ||
| - NameBinding(name='f2', allow_rename=True) <references=1> | ||
| - BuiltinBinding(name='print', allow_rename=True) <references=2> | ||
| - BuiltinBinding(name='range', allow_rename=True) <references=1> | ||
| - NameBinding(name='x', allow_rename=True) <references=3> | ||
| + Function f2 | ||
| - NameBinding(name='f', allow_rename=True) <references=1> | ||
| + Function f | ||
| - global x | ||
| - NameBinding(name='arg', allow_rename=True) <references=3> | ||
| + ListComp | ||
| - NameBinding(name='y', allow_rename=True) <references=2> | ||
| ''' | ||
| assert_namespace_tree(source, expected_namespaces) | ||
| def test_namedexpr_in_listcomp_if(): | ||
| if sys.version_info < (3, 8): | ||
| pytest.skip('Test is for >= python3.8 only') | ||
| source = ''' | ||
| def f(arg, /): | ||
| print([x for y in range(10) if (x := y // 2) & 1]) | ||
| print(arg, arg) | ||
| ''' | ||
| expected_namespaces = ''' | ||
| + Module | ||
| - NameBinding(name='f', allow_rename=True) <references=1> | ||
| - BuiltinBinding(name='print', allow_rename=True) <references=2> | ||
| - BuiltinBinding(name='range', allow_rename=True) <references=1> | ||
| + Function f | ||
| - NameBinding(name='arg', allow_rename=True) <references=3> | ||
| - NameBinding(name='x', allow_rename=True) <references=2> | ||
| + ListComp | ||
| - NameBinding(name='y', allow_rename=True) <references=2> | ||
| ''' | ||
| assert_namespace_tree(source, expected_namespaces) | ||
| def test_namedexpr_in_listcomp_body(): | ||
| if sys.version_info < (3, 8): | ||
| pytest.skip('Test is for >= python3.8 only') | ||
| source = ''' | ||
| def f(arg, /): | ||
| print([(x := y // 2) for _ in range(x)]) | ||
| print(arg, arg) | ||
| ''' | ||
| expected_namespaces = ''' | ||
| + Module | ||
| - NameBinding(name='f', allow_rename=True) <references=1> | ||
| - BuiltinBinding(name='print', allow_rename=True) <references=2> | ||
| - BuiltinBinding(name='range', allow_rename=True) <references=1> | ||
| - NameBinding(name='y', allow_rename=False) <references=1> | ||
| + Function f | ||
| - NameBinding(name='arg', allow_rename=True) <references=3> | ||
| - NameBinding(name='x', allow_rename=True) <references=2> | ||
| + ListComp | ||
| - NameBinding(name='_', allow_rename=True) <references=1> | ||
| ''' | ||
| assert_namespace_tree(source, expected_namespaces) | ||
| def test_namedexpr_in_dictcomp_body(): | ||
| if sys.version_info < (3, 8): | ||
| pytest.skip('Test is for >= python3.8 only') | ||
| source = ''' | ||
| {i: (x := i // 2) for i in range(1)} | ||
| ''' | ||
| expected_namespaces = ''' | ||
| + Module | ||
| - BuiltinBinding(name='range', allow_rename=True) <references=1> | ||
| - NameBinding(name='x', allow_rename=True) <references=1> | ||
| + DictComp | ||
| - NameBinding(name='i', allow_rename=True) <references=3> | ||
| ''' | ||
| assert_namespace_tree(source, expected_namespaces) | ||
| def test_namedexpr_in_dictcomp_if(): | ||
| if sys.version_info < (3, 8): | ||
| pytest.skip('Test is for >= python3.8 only') | ||
| source = ''' | ||
| {x: y for y in range(1) if (x := y // 2)} | ||
| ''' | ||
| expected_namespaces = ''' | ||
| + Module | ||
| - BuiltinBinding(name='range', allow_rename=True) <references=1> | ||
| - NameBinding(name='x', allow_rename=True) <references=2> | ||
| + DictComp | ||
| - NameBinding(name='y', allow_rename=True) <references=3> | ||
| ''' | ||
| assert_namespace_tree(source, expected_namespaces) | ||
| def test_namedexpr_in_setcomp_body(): | ||
| if sys.version_info < (3, 8): | ||
| pytest.skip('Test is for >= python3.8 only') | ||
| source = ''' | ||
| {(x := y // 2) for y in range(1)} | ||
| ''' | ||
| expected_namespaces = ''' | ||
| + Module | ||
| - BuiltinBinding(name='range', allow_rename=True) <references=1> | ||
| - NameBinding(name='x', allow_rename=True) <references=1> | ||
| + SetComp | ||
| - NameBinding(name='y', allow_rename=True) <references=2> | ||
| ''' | ||
| assert_namespace_tree(source, expected_namespaces) | ||
| def test_namedexpr_in_setcomp_if(): | ||
| if sys.version_info < (3, 8): | ||
| pytest.skip('Test is for >= python3.8 only') | ||
| source = ''' | ||
| {x for y in range(1) if (x := y // 2)} | ||
| ''' | ||
| expected_namespaces = ''' | ||
| + Module | ||
| - BuiltinBinding(name='range', allow_rename=True) <references=1> | ||
| - NameBinding(name='x', allow_rename=True) <references=2> | ||
| + SetComp | ||
| - NameBinding(name='y', allow_rename=True) <references=2> | ||
| ''' | ||
| assert_namespace_tree(source, expected_namespaces) | ||
| def test_namedexpr_in_generatorexp_body(): | ||
| if sys.version_info < (3, 8): | ||
| pytest.skip('Test is for >= python3.8 only') | ||
| source = ''' | ||
| ((x := y // 2) for y in range(1)) | ||
| ''' | ||
| expected_namespaces = ''' | ||
| + Module | ||
| - BuiltinBinding(name='range', allow_rename=True) <references=1> | ||
| - NameBinding(name='x', allow_rename=True) <references=1> | ||
| + GeneratorExp | ||
| - NameBinding(name='y', allow_rename=True) <references=2> | ||
| ''' | ||
| assert_namespace_tree(source, expected_namespaces) | ||
| def test_namedexpr_in_generatorexp_if(): | ||
| if sys.version_info < (3, 8): | ||
| pytest.skip('Test is for >= python3.8 only') | ||
| source = ''' | ||
| (x for y in range(1) if (x := y // 2)) | ||
| ''' | ||
| expected_namespaces = ''' | ||
| + Module | ||
| - BuiltinBinding(name='range', allow_rename=True) <references=1> | ||
| - NameBinding(name='x', allow_rename=True) <references=2> | ||
| + GeneratorExp | ||
| - NameBinding(name='y', allow_rename=True) <references=2> | ||
| ''' | ||
| assert_namespace_tree(source, expected_namespaces) |
| import sys | ||
| import pytest | ||
| import ast | ||
| from python_minifier.util import is_constant_node | ||
| def test_type_nodes(): | ||
| assert is_constant_node(ast.Str('a'), ast.Str) | ||
| if hasattr(ast, 'Bytes'): | ||
| assert is_constant_node(ast.Bytes(b'a'), ast.Bytes) | ||
| assert is_constant_node(ast.Num(1), ast.Num) | ||
| assert is_constant_node(ast.Num(0), ast.Num) | ||
| if hasattr(ast, 'NameConstant'): | ||
| assert is_constant_node(ast.NameConstant(True), ast.NameConstant) | ||
| assert is_constant_node(ast.NameConstant(False), ast.NameConstant) | ||
| assert is_constant_node(ast.NameConstant(None), ast.NameConstant) | ||
| else: | ||
| assert is_constant_node(ast.Name(id='True', ctx=ast.Load()), ast.Name) | ||
| assert is_constant_node(ast.Name(id='False', ctx=ast.Load()), ast.Name) | ||
| assert is_constant_node(ast.Name(id='None', ctx=ast.Load()), ast.Name) | ||
| assert is_constant_node(ast.Ellipsis(), ast.Ellipsis) | ||
| def test_constant_nodes(): | ||
| # only test on python 3.8+ | ||
| if sys.version_info < (3, 8): | ||
| pytest.skip('Constant not available') | ||
| assert is_constant_node(ast.Constant('a'), ast.Str) | ||
| assert is_constant_node(ast.Constant(b'a'), ast.Bytes) | ||
| assert is_constant_node(ast.Constant(1), ast.Num) | ||
| assert is_constant_node(ast.Constant(0), ast.Num) | ||
| assert is_constant_node(ast.Constant(True), ast.NameConstant) | ||
| assert is_constant_node(ast.Constant(False), ast.NameConstant) | ||
| assert is_constant_node(ast.Constant(None), ast.NameConstant) | ||
| assert is_constant_node(ast.Constant(ast.literal_eval('...')), ast.Ellipsis) |
+1
-1
| Metadata-Version: 2.1 | ||
| Name: python_minifier | ||
| Version: 2.11.2 | ||
| Version: 2.11.3 | ||
| Summary: Transform Python source code into it's most compact representation | ||
@@ -5,0 +5,0 @@ Home-page: https://github.com/dflook/python-minifier |
+1
-1
@@ -31,3 +31,3 @@ import os.path | ||
| python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, <3.14', | ||
| version='2.11.2', | ||
| version='2.11.3', | ||
@@ -34,0 +34,0 @@ classifiers=[ |
| Metadata-Version: 2.1 | ||
| Name: python_minifier | ||
| Version: 2.11.2 | ||
| Version: 2.11.3 | ||
| Summary: Transform Python source code into it's most compact representation | ||
@@ -5,0 +5,0 @@ Home-page: https://github.com/dflook/python-minifier |
@@ -23,2 +23,3 @@ LICENSE | ||
| src/python_minifier.egg-info/zip-safe | ||
| src/python_minifier/ast_annotation/__init__.py | ||
| src/python_minifier/rename/__init__.py | ||
@@ -51,2 +52,3 @@ src/python_minifier/rename/bind_names.py | ||
| test/test_bind_names.py | ||
| test/test_bind_names_namedexpr.py | ||
| test/test_bind_names_python2.py | ||
@@ -65,3 +67,3 @@ test/test_bind_names_python312.py | ||
| test/test_import.py | ||
| test/test_is_ast_node.py | ||
| test/test_is_constant_node.py | ||
| test/test_iterable_unpacking.py | ||
@@ -68,0 +70,0 @@ test/test_match.py |
@@ -7,17 +7,18 @@ """ | ||
| import python_minifier.ast_compat as ast | ||
| import re | ||
| import python_minifier.ast_compat as ast | ||
| from python_minifier.ast_annotation import add_parent | ||
| from python_minifier.ast_compare import CompareError, compare_ast | ||
| from python_minifier.module_printer import ModulePrinter | ||
| from python_minifier.rename import ( | ||
| rename_literals, | ||
| add_namespace, | ||
| allow_rename_globals, | ||
| allow_rename_locals, | ||
| bind_names, | ||
| resolve_names, | ||
| rename, | ||
| allow_rename_globals, | ||
| allow_rename_locals, | ||
| add_namespace, | ||
| rename_literals, | ||
| resolve_names | ||
| ) | ||
| from python_minifier.transforms.combine_imports import CombineImports | ||
@@ -29,4 +30,4 @@ from python_minifier.transforms.constant_folding import FoldConstants | ||
| from python_minifier.transforms.remove_debug import RemoveDebug | ||
| from python_minifier.transforms.remove_exception_brackets import remove_no_arg_exception_call | ||
| from python_minifier.transforms.remove_explicit_return_none import RemoveExplicitReturnNone | ||
| from python_minifier.transforms.remove_exception_brackets import remove_no_arg_exception_call | ||
| from python_minifier.transforms.remove_literal_statements import RemoveLiteralStatements | ||
@@ -120,2 +121,3 @@ from python_minifier.transforms.remove_object_base import RemoveObject | ||
| add_parent(module) | ||
| add_namespace(module) | ||
@@ -204,2 +206,3 @@ | ||
| def _find_shebang(source): | ||
@@ -221,2 +224,3 @@ """ | ||
| def unparse(module): | ||
@@ -223,0 +227,0 @@ """ |
| import ast | ||
| from typing import List, Text, AnyStr, Optional, Any, Union | ||
| from typing import Any, AnyStr, List, Optional, Text, Union | ||
| from .transforms.remove_annotations_options import RemoveAnnotationsOptions as RemoveAnnotationsOptions | ||
| class UnstableMinification(RuntimeError): | ||
| def __init__(self, exception: Any, source: Any, minified: Any): ... | ||
| def minify( | ||
@@ -31,4 +34,6 @@ source: AnyStr, | ||
| def unparse(module: ast.Module) -> Text: ... | ||
| def awslambda( | ||
@@ -35,0 +40,0 @@ source: AnyStr, |
@@ -10,4 +10,6 @@ from __future__ import print_function | ||
| if sys.version_info >= (3, 8): | ||
| from importlib import metadata | ||
| try: | ||
@@ -18,3 +20,4 @@ version = metadata.version('python-minifier') | ||
| else: | ||
| from pkg_resources import get_distribution, DistributionNotFound | ||
| from pkg_resources import DistributionNotFound, get_distribution | ||
| try: | ||
@@ -175,3 +178,3 @@ version = get_distribution('python_minifier').version | ||
| action='store_false', | ||
| help='Preserve any shebang line from the source', | ||
| help='Disable preserving any shebang line from the source', | ||
| dest='preserve_shebang', | ||
@@ -182,3 +185,3 @@ ) | ||
| action='store_true', | ||
| help='Remove assert statements', | ||
| help='Enable removing assert statements', | ||
| dest='remove_asserts', | ||
@@ -189,3 +192,3 @@ ) | ||
| action='store_true', | ||
| help='Remove conditional statements that test __debug__ is True', | ||
| help='Enable removing conditional statements that test __debug__ is True', | ||
| dest='remove_debug', | ||
@@ -196,3 +199,3 @@ ) | ||
| action='store_false', | ||
| help='Replace explicit return None with a bare return', | ||
| help='Disable replacing explicit return None with a bare return', | ||
| dest='remove_explicit_return_none', | ||
@@ -277,5 +280,5 @@ ) | ||
| if os.path.isdir(path_arg): | ||
| for root, dirs, files in os.walk(path_arg, onerror=error, followlinks=True): | ||
| for root, _dirs, files in os.walk(path_arg, onerror=error, followlinks=True): | ||
| for file in files: | ||
| if file.endswith('.py') or file.endswith('.pyw'): | ||
| if file.endswith(('.py', '.pyw')): | ||
| yield os.path.join(root, file) | ||
@@ -282,0 +285,0 @@ else: |
| import python_minifier.ast_compat as ast | ||
| from python_minifier.util import is_ast_node | ||
| class CompareError(RuntimeError): | ||
@@ -21,3 +19,3 @@ """ | ||
| if hasattr(node, 'namespace'): | ||
| if is_ast_node(node.namespace, (ast.FunctionDef, ast.ClassDef, 'AsyncFunctionDef')): | ||
| if isinstance(node.namespace, (ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef)): | ||
| return self.namespace(node.namespace) + '.' + node.namespace.name | ||
@@ -24,0 +22,0 @@ elif isinstance(node.namespace, ast.Module): |
@@ -11,2 +11,3 @@ """ | ||
| # Ideally we don't import anything else | ||
@@ -20,2 +21,3 @@ | ||
| # These classes are redefined from the ones in ast that complain about deprecation | ||
@@ -28,2 +30,3 @@ # They will continue to work once they are removed from ast | ||
| class Bytes(Constant): # type: ignore[no-redef] | ||
@@ -33,2 +36,3 @@ def __new__(cls, s, *args, **kwargs): | ||
| class Num(Constant): # type: ignore[no-redef] | ||
@@ -38,2 +42,3 @@ def __new__(cls, n, *args, **kwargs): | ||
| class NameConstant(Constant): # type: ignore[no-redef] | ||
@@ -43,4 +48,37 @@ def __new__(cls, *args, **kwargs): | ||
| class Ellipsis(Constant): # type: ignore[no-redef] | ||
| def __new__(cls, *args, **kwargs): | ||
| return Constant(value=literal_eval('...'), *args, **kwargs) | ||
| # Create a dummy class for missing AST nodes | ||
| for _node_type in [ | ||
| 'AnnAssign', | ||
| 'AsyncFor', | ||
| 'AsyncFunctionDef', | ||
| 'AsyncFunctionDef', | ||
| 'AsyncWith', | ||
| 'Bytes', | ||
| 'Constant', | ||
| 'DictComp', | ||
| 'Exec', | ||
| 'ListComp', | ||
| 'MatchAs', | ||
| 'MatchMapping', | ||
| 'MatchStar', | ||
| 'NameConstant', | ||
| 'NamedExpr', | ||
| 'Nonlocal', | ||
| 'ParamSpec', | ||
| 'SetComp', | ||
| 'Starred', | ||
| 'TryStar', | ||
| 'TypeVar', | ||
| 'TypeVarTuple', | ||
| 'YieldFrom', | ||
| 'arg', | ||
| 'withitem', | ||
| ]: | ||
| if _node_type not in globals(): | ||
| globals()[_node_type] = type(_node_type, (AST,), {}) |
@@ -15,4 +15,5 @@ """ | ||
| from python_minifier.util import is_ast_node | ||
| from python_minifier.util import is_constant_node | ||
| INDENT = ' ' | ||
@@ -67,2 +68,3 @@ | ||
| def is_literal(node, field): | ||
@@ -72,12 +74,12 @@ if hasattr(ast, 'Constant') and isinstance(node, ast.Constant) and field == 'value': | ||
| if is_ast_node(node, ast.Num) and field == 'n': | ||
| if is_constant_node(node, ast.Num) and field == 'n': | ||
| return True | ||
| if is_ast_node(node, ast.Str) and field == 's': | ||
| if is_constant_node(node, ast.Str) and field == 's': | ||
| return True | ||
| if is_ast_node(node, 'Bytes') and field == 's': | ||
| if is_constant_node(node, ast.Bytes) and field == 's': | ||
| return True | ||
| if is_ast_node(node, 'NameConstant') and field == 'value': | ||
| if is_constant_node(node, ast.NameConstant) and field == 'value': | ||
| return True | ||
@@ -87,2 +89,3 @@ | ||
| def print_ast(node): | ||
@@ -89,0 +92,0 @@ if not isinstance(node, ast.AST): |
@@ -1,7 +0,7 @@ | ||
| import python_minifier.ast_compat as ast | ||
| import sys | ||
| from python_minifier.util import is_ast_node | ||
| import python_minifier.ast_compat as ast | ||
| from python_minifier.token_printer import TokenPrinter, Delimiter | ||
| from python_minifier.token_printer import Delimiter, TokenPrinter | ||
| from python_minifier.util import is_constant_node | ||
@@ -72,3 +72,3 @@ | ||
| # Make sure the Num get the precedence of the USub operator in this case. | ||
| if sys.version_info < (3, 0) and is_ast_node(node, ast.Num): | ||
| if sys.version_info < (3, 0) and is_constant_node(node, ast.Num): | ||
| if str(node.n)[0] == '-': | ||
@@ -160,3 +160,3 @@ return self.precedences['USub'] | ||
| if 0 < self.precedence(datum) <=7: | ||
| if 0 < self.precedence(datum) <= 7: | ||
| self.printer.delimiter('(') | ||
@@ -213,3 +213,3 @@ self._expression(datum) | ||
| if sys.version_info < (3, 0) and isinstance(node.op, ast.USub) and is_ast_node(node.operand, ast.Num): | ||
| if sys.version_info < (3, 0) and isinstance(node.op, ast.USub) and is_constant_node(node.operand, ast.Num): | ||
| # For: -(1), which is parsed as a UnaryOp(USub, Num(1)). | ||
@@ -434,3 +434,3 @@ # Without this special case it would be printed as -1 | ||
| if (value_precedence != 0 and (attr_precedence > value_precedence)) or is_ast_node(node.value, ast.Num): | ||
| if (value_precedence != 0 and (attr_precedence > value_precedence)) or is_constant_node(node.value, ast.Num): | ||
| self.printer.delimiter('(') | ||
@@ -469,3 +469,3 @@ self._expression(node.value) | ||
| self.visit_ExtSlice(node.slice) | ||
| elif is_ast_node(node.slice, ast.Ellipsis): | ||
| elif is_constant_node(node.slice, ast.Ellipsis): | ||
| self.visit_Ellipsis(node) | ||
@@ -637,3 +637,4 @@ elif sys.version_info >= (3, 9) and isinstance(node.slice, ast.Tuple): | ||
| # Python 2 uses Name nodes | ||
| return self.visit_Name(node) | ||
| self.visit_Name(node) | ||
| return | ||
@@ -657,3 +658,3 @@ self.printer.identifier(node.arg) | ||
| def _expression(self, expression): | ||
| if is_ast_node(expression, (ast.Yield, 'YieldFrom')): | ||
| if isinstance(expression, (ast.Yield, ast.YieldFrom)): | ||
| self.printer.delimiter('(') | ||
@@ -666,3 +667,3 @@ self._yield_expr(expression) | ||
| self.printer.delimiter(')') | ||
| elif is_ast_node(expression, 'NamedExpr'): | ||
| elif isinstance(expression, ast.NamedExpr): | ||
| self.printer.delimiter('(') | ||
@@ -675,7 +676,7 @@ self.visit_NamedExpr(expression) | ||
| def _testlist(self, test): | ||
| if is_ast_node(test, (ast.Yield, 'YieldFrom')): | ||
| if isinstance(test, (ast.Yield, ast.YieldFrom)): | ||
| self.printer.delimiter('(') | ||
| self._yield_expr(test) | ||
| self.printer.delimiter(')') | ||
| elif is_ast_node(test, 'NamedExpr'): | ||
| elif isinstance(test, ast.NamedExpr): | ||
| self.printer.delimiter('(') | ||
@@ -682,0 +683,0 @@ self.visit_NamedExpr(test) |
@@ -9,13 +9,13 @@ """ | ||
| import python_minifier.ast_compat as ast | ||
| import copy | ||
| import re | ||
| import python_minifier.ast_compat as ast | ||
| from python_minifier import UnstableMinification | ||
| from python_minifier.ast_compare import CompareError | ||
| from python_minifier.ast_compare import compare_ast | ||
| from python_minifier.ast_compare import CompareError, compare_ast | ||
| from python_minifier.expression_printer import ExpressionPrinter | ||
| from python_minifier.ministring import MiniString | ||
| from python_minifier.token_printer import TokenTypes | ||
| from python_minifier.util import is_ast_node | ||
| from python_minifier.util import is_constant_node | ||
@@ -40,3 +40,3 @@ | ||
| return True | ||
| except Exception as e: | ||
| except Exception: | ||
| return False | ||
@@ -75,3 +75,3 @@ | ||
| for v in self.node.values: | ||
| if is_ast_node(v, ast.Str): | ||
| if is_constant_node(v, ast.Str): | ||
@@ -85,3 +85,3 @@ # Could this be used as a debug specifier? | ||
| debug_specifier_candidates = [x + '{' + v.s for x in candidates] | ||
| except Exception as e: | ||
| except Exception: | ||
| continue | ||
@@ -91,3 +91,3 @@ | ||
| candidates = [x + self.str_for(v.s, quote) for x in candidates] | ||
| except Exception as e: | ||
| except Exception: | ||
| continue | ||
@@ -101,3 +101,3 @@ elif isinstance(v, ast.FormattedValue): | ||
| debug_specifier_candidates = [] | ||
| except Exception as e: | ||
| except Exception: | ||
| continue | ||
@@ -109,4 +109,3 @@ else: | ||
| actual_candidates = filter(self.is_correct_ast, actual_candidates) | ||
| return actual_candidates | ||
| return filter(self.is_correct_ast, actual_candidates) | ||
@@ -280,3 +279,3 @@ def str_for(self, s, quote): | ||
| raise ValueError('Couldn\'t find a quote') | ||
| raise ValueError("Couldn't find a quote") | ||
@@ -360,3 +359,3 @@ def _literals(self): | ||
| for v in self.node.values: | ||
| if is_ast_node(v, ast.Str): | ||
| if is_constant_node(v, ast.Str): | ||
| candidates = [x + self.str_for(v.s) for x in candidates] | ||
@@ -409,3 +408,3 @@ elif isinstance(v, ast.FormattedValue): | ||
| raise ValueError('Couldn\'t find a quote') | ||
| raise ValueError("Couldn't find a quote") | ||
@@ -412,0 +411,0 @@ def _literals(self): |
@@ -35,3 +35,3 @@ BACKSLASH = '\\' | ||
| eval(self.quote + s + self.quote) | ||
| except (UnicodeDecodeError, UnicodeEncodeError) as e: | ||
| except (UnicodeDecodeError, UnicodeEncodeError): | ||
| if self.safe_mode: | ||
@@ -67,3 +67,3 @@ raise | ||
| for c in self._s: | ||
| if c in escaped.keys(): | ||
| if c in escaped: | ||
| s += escaped[c] | ||
@@ -100,3 +100,3 @@ else: | ||
| for c in self._s: | ||
| if c in escaped.keys(): | ||
| if c in escaped: | ||
| s += escaped[c] | ||
@@ -103,0 +103,0 @@ else: |
@@ -1,7 +0,7 @@ | ||
| import python_minifier.ast_compat as ast | ||
| import sys | ||
| import python_minifier.ast_compat as ast | ||
| from .expression_printer import ExpressionPrinter | ||
| from .token_printer import Delimiter | ||
| from .util import is_ast_node | ||
@@ -58,3 +58,3 @@ | ||
| if is_ast_node(node.value, (ast.Yield, 'YieldFrom')): | ||
| if isinstance(node.value, (ast.Yield, ast.YieldFrom)): | ||
| self._yield_expr(node.value) | ||
@@ -86,3 +86,3 @@ else: | ||
| # Yield nodes that are the sole node on the right hand side of an assignment do not need parens | ||
| if is_ast_node(node.value, (ast.Yield, 'YieldFrom')): | ||
| if isinstance(node.value, (ast.Yield, ast.YieldFrom)): | ||
| self._yield_expr(node.value) | ||
@@ -102,3 +102,3 @@ else: | ||
| # Yield nodes that are the sole node on the right hand side of an assignment do not need parens | ||
| if is_ast_node(node.value, (ast.Yield, 'YieldFrom')): | ||
| if isinstance(node.value, (ast.Yield, ast.YieldFrom)): | ||
| self._yield_expr(node.value) | ||
@@ -149,3 +149,3 @@ else: | ||
| if isinstance(node.value, ast.Tuple): | ||
| if sys.version_info < (3, 8) and [n for n in node.value.elts if is_ast_node(n, 'Starred')]: | ||
| if sys.version_info < (3, 8) and [n for n in node.value.elts if isinstance(n, ast.Starred)]: | ||
| self.printer.delimiter('(') | ||
@@ -250,3 +250,3 @@ self._testlist(node.value) | ||
| self.printer.keyword('from') | ||
| for i in range(node.level): | ||
| for _i in range(node.level): | ||
| self.printer.delimiter('.') | ||
@@ -334,3 +334,3 @@ if node.module is not None: | ||
| def visit_For(self, node, is_async=False): | ||
| assert isinstance(node, ast.For) or (hasattr(ast, 'AsyncFor') and isinstance(node, ast.AsyncFor)) | ||
| assert isinstance(node, (ast.For, ast.AsyncFor)) | ||
@@ -371,3 +371,3 @@ self.printer.newline() | ||
| def visit_Try(self, node, star=False): | ||
| assert is_ast_node(node, (ast.Try, 'TryStar')) | ||
| assert isinstance(node, (ast.Try, ast.TryStar)) | ||
@@ -450,3 +450,3 @@ self.printer.newline() | ||
| def visit_With(self, node, is_async=False): | ||
| assert is_ast_node(node, (ast.With, 'AsyncWith')) | ||
| assert isinstance(node, (ast.With, ast.AsyncWith)) | ||
@@ -480,3 +480,3 @@ self.printer.newline() | ||
| def visit_withitem(self, node): | ||
| assert is_ast_node(node, ('withitem', ast.With)) | ||
| assert isinstance(node, (ast.withitem, ast.With)) | ||
@@ -490,3 +490,3 @@ self._expression(node.context_expr) | ||
| def visit_FunctionDef(self, node, is_async=False): | ||
| assert is_ast_node(node, (ast.FunctionDef, 'AsyncFunctionDef')) | ||
| assert isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) | ||
@@ -683,3 +683,3 @@ self.printer.newline() | ||
| self.printer.identifier(kwd) | ||
| self.printer.identifier(kwd) | ||
| self.printer.delimiter('=') | ||
@@ -873,2 +873,1 @@ | ||
| statements[node.__class__.__name__](node) | ||
@@ -6,2 +6,2 @@ from python_minifier.rename.bind_names import bind_names | ||
| from python_minifier.rename.resolve_names import resolve_names | ||
| from python_minifier.rename.util import allow_rename_locals, allow_rename_globals | ||
| from python_minifier.rename.util import allow_rename_globals, allow_rename_locals |
| import python_minifier.ast_compat as ast | ||
| from python_minifier.rename.binding import NameBinding | ||
| from python_minifier.rename.util import arg_rename_in_place, get_global_namespace, get_nonlocal_namespace, builtins | ||
| from python_minifier.rename.util import arg_rename_in_place, builtins, get_global_namespace | ||
| from python_minifier.transforms.suite_transformer import NodeVisitor | ||
@@ -183,2 +183,3 @@ | ||
| def bind_names(module): | ||
@@ -185,0 +186,0 @@ """ |
| import python_minifier.ast_compat as ast | ||
| from python_minifier.rename.util import arg_rename_in_place, insert | ||
| from python_minifier.util import is_ast_node | ||
@@ -108,7 +107,7 @@ | ||
| arg_rename = True | ||
| elif is_ast_node(node, (ast.ClassDef, ast.FunctionDef, 'AsyncFunctionDef')): | ||
| elif isinstance(node, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)): | ||
| pass | ||
| elif isinstance(node, ast.ExceptHandler): | ||
| pass | ||
| elif is_ast_node(node, (ast.Global, 'Nonlocal')): | ||
| elif isinstance(node, (ast.Global, ast.Nonlocal)): | ||
| pass | ||
@@ -123,18 +122,18 @@ elif isinstance(node, ast.alias): | ||
| pass | ||
| elif is_ast_node(node, 'arg'): | ||
| elif isinstance(node, ast.arg): | ||
| if not arg_rename_in_place(node): | ||
| arg_rename = True | ||
| elif is_ast_node(node, 'MatchAs'): | ||
| elif isinstance(node, ast.MatchAs): | ||
| if node.name is None: | ||
| additional_bytes += 4 # ' as ' | ||
| elif is_ast_node(node, 'MatchStar'): | ||
| elif isinstance(node, ast.MatchStar): | ||
| pass | ||
| elif is_ast_node(node, 'MatchMapping'): | ||
| elif isinstance(node, ast.MatchMapping): | ||
| pass | ||
| elif is_ast_node(node, 'TypeVar'): | ||
| elif isinstance(node, ast.TypeVar): | ||
| pass | ||
| elif is_ast_node(node, 'TypeVarTuple'): | ||
| elif isinstance(node, ast.TypeVarTuple): | ||
| pass | ||
| elif is_ast_node(node, 'ParamSpec'): | ||
| elif isinstance(node, ast.ParamSpec): | ||
| pass | ||
@@ -165,7 +164,7 @@ | ||
| elif is_ast_node(node, (ast.ClassDef, ast.FunctionDef, 'AsyncFunctionDef')): | ||
| elif isinstance(node, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)): | ||
| pass | ||
| elif isinstance(node, ast.ExceptHandler): | ||
| pass | ||
| elif is_ast_node(node, (ast.Global, 'Nonlocal')): | ||
| elif isinstance(node, (ast.Global, ast.Nonlocal)): | ||
| pass | ||
@@ -178,3 +177,3 @@ elif isinstance(node, ast.alias): | ||
| pass | ||
| elif is_ast_node(node, 'arg'): | ||
| elif isinstance(node, ast.arg): | ||
| if not arg_rename_in_place(node): | ||
@@ -184,13 +183,13 @@ mentions += 1 | ||
| elif is_ast_node(node, 'MatchAs'): | ||
| elif isinstance(node, ast.MatchAs): | ||
| pass | ||
| elif is_ast_node(node, 'MatchStar'): | ||
| elif isinstance(node, ast.MatchStar): | ||
| pass | ||
| elif is_ast_node(node, 'MatchMapping'): | ||
| elif isinstance(node, ast.MatchMapping): | ||
| pass | ||
| elif is_ast_node(node, 'TypeVar'): | ||
| elif isinstance(node, ast.TypeVar): | ||
| pass | ||
| elif is_ast_node(node, 'TypeVarTuple'): | ||
| elif isinstance(node, ast.TypeVarTuple): | ||
| pass | ||
| elif is_ast_node(node, 'ParamSpec'): | ||
| elif isinstance(node, ast.ParamSpec): | ||
| pass | ||
@@ -218,7 +217,7 @@ | ||
| arg_rename = True | ||
| elif is_ast_node(node, (ast.ClassDef, ast.FunctionDef, 'AsyncFunctionDef')): | ||
| elif isinstance(node, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)): | ||
| mentions += 1 | ||
| elif isinstance(node, ast.ExceptHandler): | ||
| mentions += 1 | ||
| elif is_ast_node(node, (ast.Global, 'Nonlocal')): | ||
| elif isinstance(node, (ast.Global, ast.Nonlocal)): | ||
| mentions += len([n for n in node.names if n == self._name]) | ||
@@ -232,16 +231,16 @@ elif isinstance(node, ast.alias): | ||
| mentions += 1 | ||
| elif is_ast_node(node, 'arg'): | ||
| elif isinstance(node, ast.arg): | ||
| arg_rename = True | ||
| elif is_ast_node(node, 'MatchAs'): | ||
| elif isinstance(node, ast.MatchAs): | ||
| mentions += 1 | ||
| elif is_ast_node(node, 'MatchStar'): | ||
| elif isinstance(node, ast.MatchStar): | ||
| mentions += 1 | ||
| elif is_ast_node(node, 'MatchMapping'): | ||
| elif isinstance(node, ast.MatchMapping): | ||
| mentions += 1 | ||
| elif is_ast_node(node, 'TypeVar'): | ||
| elif isinstance(node, ast.TypeVar): | ||
| mentions += 1 | ||
| elif is_ast_node(node, 'TypeVarTuple'): | ||
| elif isinstance(node, ast.TypeVarTuple): | ||
| mentions += 1 | ||
| elif is_ast_node(node, 'ParamSpec'): | ||
| elif isinstance(node, ast.ParamSpec): | ||
| mentions += 1 | ||
@@ -283,3 +282,3 @@ | ||
| raise NotImplementedError() | ||
| raise NotImplementedError | ||
@@ -294,3 +293,3 @@ def rename(self, new_name): | ||
| raise NotImplementedError() | ||
| raise NotImplementedError | ||
@@ -374,3 +373,3 @@ | ||
| elif is_ast_node(node, (ast.FunctionDef, 'AsyncFunctionDef')): | ||
| elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): | ||
| node.name = new_name | ||
@@ -384,3 +383,3 @@ elif isinstance(node, ast.ClassDef): | ||
| node.asname = new_name | ||
| elif is_ast_node(node, 'arg'): | ||
| elif isinstance(node, ast.arg): | ||
@@ -398,3 +397,3 @@ if arg_rename_in_place(node): | ||
| node.name = new_name | ||
| elif is_ast_node(node, (ast.Global, 'Nonlocal')): | ||
| elif isinstance(node, (ast.Global, ast.Nonlocal)): | ||
| node.names = [new_name if n == self._name else n for n in node.names] | ||
@@ -413,13 +412,13 @@ elif isinstance(node, ast.arguments): | ||
| elif is_ast_node(node, 'MatchAs'): | ||
| elif isinstance(node, ast.MatchAs): | ||
| node.name = new_name | ||
| elif is_ast_node(node, 'MatchStar'): | ||
| elif isinstance(node, ast.MatchStar): | ||
| node.name = new_name | ||
| elif is_ast_node(node, 'MatchMapping'): | ||
| elif isinstance(node, ast.MatchMapping): | ||
| node.rest = new_name | ||
| elif is_ast_node(node, 'TypeVar'): | ||
| elif isinstance(node, ast.TypeVar): | ||
| node.name = new_name | ||
| elif is_ast_node(node, 'TypeVarTuple'): | ||
| elif isinstance(node, ast.TypeVarTuple): | ||
| node.name = new_name | ||
| elif is_ast_node(node, 'ParamSpec'): | ||
| elif isinstance(node, ast.ParamSpec): | ||
| node.name = new_name | ||
@@ -508,2 +507,2 @@ | ||
| return False | ||
| return False |
@@ -6,44 +6,43 @@ """ | ||
| import python_minifier.ast_compat as ast | ||
| from python_minifier.ast_annotation import get_parent | ||
| from python_minifier.rename.util import is_namespace | ||
| from python_minifier.util import is_ast_node | ||
| def add_parent_to_arguments(arguments, func): | ||
| arguments.parent = func | ||
| arguments.namespace = func | ||
| for arg in getattr(arguments, 'posonlyargs', []) + arguments.args: | ||
| add_parent(arg, arguments, func) | ||
| add_parent(arg, func) | ||
| if hasattr(arg, 'annotation') and arg.annotation is not None: | ||
| add_parent(arg.annotation, arguments, func.namespace) | ||
| add_parent(arg.annotation, func.namespace) | ||
| if hasattr(arguments, 'kwonlyargs'): | ||
| for arg in arguments.kwonlyargs: | ||
| add_parent(arg, arguments, func) | ||
| add_parent(arg, func) | ||
| if arg.annotation is not None: | ||
| add_parent(arg.annotation, arguments, func.namespace) | ||
| add_parent(arg.annotation, func.namespace) | ||
| for node in arguments.kw_defaults: | ||
| if node is not None: | ||
| add_parent(node, arguments, func.namespace) | ||
| add_parent(node, func.namespace) | ||
| for node in arguments.defaults: | ||
| add_parent(node, arguments, func.namespace) | ||
| add_parent(node, func.namespace) | ||
| if arguments.vararg: | ||
| if hasattr(arguments, 'varargannotation') and arguments.varargannotation is not None: | ||
| add_parent(arguments.varargannotation, arguments, func.namespace) | ||
| add_parent(arguments.varargannotation, func.namespace) | ||
| elif isinstance(arguments.vararg, str): | ||
| pass | ||
| else: | ||
| add_parent(arguments.vararg, arguments, func) | ||
| add_parent(arguments.vararg, func) | ||
| if arguments.kwarg: | ||
| if hasattr(arguments, 'kwargannotation') and arguments.kwargannotation is not None: | ||
| add_parent(arguments.kwargannotation, arguments, func.namespace) | ||
| add_parent(arguments.kwargannotation, func.namespace) | ||
| elif isinstance(arguments.kwarg, str): | ||
| pass | ||
| else: | ||
| add_parent(arguments.kwarg, arguments, func) | ||
| add_parent(arguments.kwarg, func) | ||
@@ -60,13 +59,13 @@ | ||
| for node in functiondef.body: | ||
| add_parent(node, parent=functiondef, namespace=functiondef) | ||
| add_parent(node, namespace=functiondef) | ||
| for node in functiondef.decorator_list: | ||
| add_parent(node, parent=functiondef, namespace=functiondef.namespace) | ||
| add_parent(node, namespace=functiondef.namespace) | ||
| if hasattr(functiondef, 'type_params') and functiondef.type_params is not None: | ||
| for node in functiondef.type_params: | ||
| add_parent(node, parent=functiondef, namespace=functiondef.namespace) | ||
| add_parent(node, namespace=functiondef.namespace) | ||
| if hasattr(functiondef, 'returns') and functiondef.returns is not None: | ||
| add_parent(functiondef.returns, parent=functiondef, namespace=functiondef.namespace) | ||
| add_parent(functiondef.returns, namespace=functiondef.namespace) | ||
@@ -80,54 +79,68 @@ | ||
| for node in classdef.bases: | ||
| add_parent(node, parent=classdef, namespace=classdef.namespace) | ||
| add_parent(node, namespace=classdef.namespace) | ||
| if hasattr(classdef, 'keywords'): | ||
| for node in classdef.keywords: | ||
| add_parent(node, parent=classdef, namespace=classdef.namespace) | ||
| add_parent(node, namespace=classdef.namespace) | ||
| if hasattr(classdef, 'starargs') and classdef.starargs is not None: | ||
| add_parent(classdef.starargs, parent=classdef, namespace=classdef.namespace) | ||
| add_parent(classdef.starargs, namespace=classdef.namespace) | ||
| if hasattr(classdef, 'kwargs') and classdef.kwargs is not None: | ||
| add_parent(classdef.kwargs, parent=classdef, namespace=classdef.namespace) | ||
| add_parent(classdef.kwargs, namespace=classdef.namespace) | ||
| for node in classdef.body: | ||
| add_parent(node, parent=classdef, namespace=classdef) | ||
| add_parent(node, namespace=classdef) | ||
| for node in classdef.decorator_list: | ||
| add_parent(node, parent=classdef, namespace=classdef.namespace) | ||
| add_parent(node, namespace=classdef.namespace) | ||
| if hasattr(classdef, 'type_params') and classdef.type_params is not None: | ||
| for node in classdef.type_params: | ||
| add_parent(node, parent=classdef, namespace=classdef.namespace) | ||
| add_parent(node, namespace=classdef.namespace) | ||
| def add_parent_to_comprehension(node, namespace): | ||
| assert is_ast_node(node, (ast.GeneratorExp, 'SetComp', 'DictComp', 'ListComp')) | ||
| assert isinstance(node, (ast.GeneratorExp, ast.SetComp, ast.DictComp, ast.ListComp)) | ||
| if hasattr(node, 'elt'): | ||
| add_parent(node.elt, parent=node, namespace=node) | ||
| add_parent(node.elt, namespace=node) | ||
| elif hasattr(node, 'key'): | ||
| add_parent(node.key, parent=node, namespace=node) | ||
| add_parent(node.value, parent=node, namespace=node) | ||
| add_parent(node.key, namespace=node) | ||
| add_parent(node.value, namespace=node) | ||
| iter_namespace = namespace | ||
| for generator in node.generators: | ||
| generator.parent = node | ||
| generator.namespace = node | ||
| add_parent(generator.target, parent=generator, namespace=node) | ||
| add_parent(generator.iter, parent=generator, namespace=iter_namespace) | ||
| iter_namespace = node | ||
| add_parent(generator.target, namespace=node) | ||
| add_parent(generator.iter, namespace=iter_namespace) | ||
| for if_ in generator.ifs: | ||
| add_parent(if_, parent=generator, namespace=node) | ||
| add_parent(if_, namespace=node) | ||
| iter_namespace = node | ||
| def add_parent(node, parent=None, namespace=None): | ||
| def namedexpr_namespace(node): | ||
| """ | ||
| Add a parent attribute to child nodes | ||
| Get the namespace for a NamedExpr target | ||
| """ | ||
| if not isinstance(node, (ast.ListComp, ast.DictComp, ast.SetComp, ast.GeneratorExp)): | ||
| return node | ||
| return namedexpr_namespace(node.namespace) | ||
| def add_parent_to_namedexpr(node): | ||
| assert isinstance(node, ast.NamedExpr) | ||
| add_parent(node.target, namespace=namedexpr_namespace(node.namespace)) | ||
| add_parent(node.value, namespace=node.namespace) | ||
| def add_parent(node, namespace=None): | ||
| """ | ||
| Add a namespace attribute to child nodes | ||
| :param node: The tree to add parent and namespace properties to | ||
| :param node: The tree to add namespace properties to | ||
| :type node: :class:`ast.AST` | ||
| :param parent: The parent node of this node | ||
| :type parent: :class:`ast.AST` | ||
| :param namespace: The namespace Node that this node is in | ||
@@ -138,3 +151,2 @@ :type namespace: ast.Lambda or ast.Module or ast.FunctionDef or ast.AsyncFunctionDef or ast.ClassDef or ast.DictComp or ast.SetComp or ast.ListComp or ast.Generator | ||
| node.parent = parent if parent is not None else node | ||
| node.namespace = namespace if namespace is not None else node | ||
@@ -147,9 +159,9 @@ | ||
| if is_ast_node(node, (ast.FunctionDef, 'AsyncFunctionDef')): | ||
| if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): | ||
| add_parent_to_functiondef(node) | ||
| elif is_ast_node(node, (ast.GeneratorExp, 'SetComp', 'DictComp', 'ListComp')): | ||
| elif isinstance(node, (ast.GeneratorExp, ast.SetComp, ast.DictComp, ast.ListComp)): | ||
| add_parent_to_comprehension(node, namespace=namespace) | ||
| elif isinstance(node, ast.Lambda): | ||
| add_parent_to_arguments(node.args, func=node) | ||
| add_parent(node.body, parent=node, namespace=node) | ||
| add_parent(node.body, namespace=node) | ||
| elif isinstance(node, ast.ClassDef): | ||
@@ -159,3 +171,3 @@ add_parent_to_classdef(node) | ||
| for child in ast.iter_child_nodes(node): | ||
| add_parent(child, parent=node, namespace=node) | ||
| add_parent(child, namespace=node) | ||
@@ -166,14 +178,18 @@ return | ||
| namespace.global_names.update(node.names) | ||
| if is_ast_node(node, 'Nonlocal'): | ||
| if isinstance(node, ast.Nonlocal): | ||
| namespace.nonlocal_names.update(node.names) | ||
| if isinstance(node, ast.Name): | ||
| if isinstance(namespace, ast.ClassDef): | ||
| if isinstance(node.ctx, ast.Load): | ||
| namespace.nonlocal_names.add(node.id) | ||
| elif isinstance(node.ctx, ast.Store) and isinstance(node.parent, ast.AugAssign): | ||
| namespace.nonlocal_names.add(node.id) | ||
| if isinstance(node, ast.Name) and isinstance(namespace, ast.ClassDef): | ||
| if isinstance(node.ctx, ast.Load): | ||
| namespace.nonlocal_names.add(node.id) | ||
| elif isinstance(node.ctx, ast.Store) and isinstance(get_parent(node), ast.AugAssign): | ||
| namespace.nonlocal_names.add(node.id) | ||
| if isinstance(node, ast.NamedExpr): | ||
| # NamedExpr is 'special' | ||
| add_parent_to_namedexpr(node) | ||
| return | ||
| for child in ast.iter_child_nodes(node): | ||
| add_parent(child, parent=node, namespace=namespace) | ||
| add_parent(child, namespace=namespace) | ||
@@ -180,0 +196,0 @@ |
| import python_minifier.ast_compat as ast | ||
| from python_minifier.ast_annotation import get_parent, set_parent | ||
@@ -6,8 +7,8 @@ from python_minifier.rename.binding import Binding | ||
| from python_minifier.transforms.suite_transformer import NodeVisitor | ||
| from python_minifier.util import is_ast_node | ||
| from python_minifier.util import is_constant_node | ||
| def replace(old_node, new_node): | ||
| parent = old_node.parent | ||
| new_node.parent = parent | ||
| parent = get_parent(old_node) | ||
| set_parent(new_node, parent) | ||
| new_node.namespace = old_node.namespace | ||
@@ -47,3 +48,3 @@ | ||
| def value(self): | ||
| if is_ast_node(self._value_node, (ast.Str, 'Bytes')): | ||
| if is_constant_node(self._value_node, (ast.Str, ast.Bytes)): | ||
| return self._value_node.s | ||
@@ -89,2 +90,3 @@ else: | ||
| class HoistedValue(object): | ||
@@ -138,3 +140,3 @@ """ | ||
| if is_ast_node(node.namespace, (ast.FunctionDef, ast.Module, 'AsyncFunctionDef')): | ||
| if isinstance(node.namespace, (ast.FunctionDef, ast.Module, ast.AsyncFunctionDef)): | ||
| return node.namespace | ||
@@ -161,7 +163,7 @@ return self.nearest_function_namespace(node.namespace) | ||
| l = [] | ||
| path = [] | ||
| while True: | ||
| namespace = self.nearest_function_namespace(node) | ||
| l.insert(0, namespace) | ||
| path.insert(0, namespace) | ||
@@ -173,3 +175,3 @@ if isinstance(namespace, ast.Module): | ||
| return l | ||
| return path | ||
@@ -210,3 +212,3 @@ def common_path(self, n1_path, n2_path): | ||
| if isinstance(node.parent, ast.Expr): | ||
| if isinstance(get_parent(node), ast.Expr): | ||
| # This is literal statement | ||
@@ -223,7 +225,6 @@ # The RemoveLiteralStatements transformer must have left it here, so ignore it. | ||
| for v in node.values: | ||
| if is_ast_node(v, ast.Str): | ||
| if is_constant_node(v, ast.Str): | ||
| # Can't hoist this! | ||
| continue | ||
| else: | ||
| self.visit(v) | ||
| self.visit(v) | ||
@@ -246,13 +247,14 @@ def visit_NameConstant(self, node): | ||
| if not is_ast_node(node.namespace, ast.ClassDef): | ||
| if not isinstance(node.namespace, ast.ClassDef): | ||
| return self.generic_visit(node) | ||
| for target in node.targets: | ||
| if is_ast_node(target, ast.Name) and target.id == '__slots__': | ||
| if isinstance(target, ast.Name) and target.id == '__slots__': | ||
| # This is a __slots__ assignment, don't hoist the literals | ||
| return | ||
| return None | ||
| return self.generic_visit(node) | ||
| def rename_literals(module): | ||
| HoistLiterals()(module) |
@@ -58,3 +58,3 @@ import python_minifier.ast_compat as ast | ||
| namespaces = set([namespace]) | ||
| namespaces = {namespace} | ||
@@ -116,3 +116,3 @@ for node in binding.references: | ||
| for namespace, binding in sorted_bindings(module): | ||
| for _namespace, binding in sorted_bindings(module): | ||
| if binding.allow_rename: | ||
@@ -159,2 +159,4 @@ binding.new_name = self.available_name() | ||
| return None | ||
| def is_available(self, name, reservation_scope): | ||
@@ -171,8 +173,4 @@ """ | ||
| for namespace in reservation_scope: | ||
| if name in namespace.assigned_names: | ||
| return False | ||
| return all(name not in namespace.assigned_names for namespace in reservation_scope) | ||
| return True | ||
| def __call__(self, module, prefix_globals, reserved_globals=None): | ||
@@ -191,2 +189,22 @@ assert isinstance(module, ast.Module) | ||
| def should_rename(binding, name, scope): | ||
| if binding.should_rename(name): | ||
| return True | ||
| # It's no longer efficient to do this rename | ||
| if isinstance(binding, NameBinding): | ||
| # Check that the original name is still available | ||
| if binding.reserved == binding.name: | ||
| # We already reserved it (this is probably an arg) | ||
| return False | ||
| if not self.is_available(binding.name, scope): | ||
| # The original name has already been assigned to another binding, | ||
| # so we need to rename this anyway. | ||
| return True | ||
| return False | ||
| for namespace, binding in sorted_bindings(module): | ||
@@ -202,23 +220,3 @@ scope = reservation_scope(namespace, binding) | ||
| def should_rename(): | ||
| if binding.should_rename(name): | ||
| return True | ||
| # It's no longer efficient to do this rename | ||
| if isinstance(binding, NameBinding): | ||
| # Check that the original name is still available | ||
| if binding.reserved == binding.name: | ||
| # We already reserved it (this is probably an arg) | ||
| return False | ||
| if not self.is_available(binding.name, scope): | ||
| # The original name has already been assigned to another binding, | ||
| # so we need to rename this anyway. | ||
| return True | ||
| return False | ||
| if should_rename(): | ||
| if should_rename(binding, name, scope): | ||
| binding.rename(name) | ||
@@ -225,0 +223,0 @@ else: |
| import python_minifier.ast_compat as ast | ||
| from python_minifier.rename.binding import BuiltinBinding, NameBinding | ||
| from python_minifier.rename.util import get_global_namespace, get_nonlocal_namespace, builtins | ||
| from python_minifier.util import is_ast_node | ||
| from python_minifier.rename.util import builtins, get_global_namespace, get_nonlocal_namespace | ||
@@ -37,2 +36,3 @@ | ||
| def get_binding_disallow_class_namespace_rename(name, namespace): | ||
@@ -47,2 +47,3 @@ binding = get_binding(name, namespace) | ||
| def resolve_names(node): | ||
@@ -70,3 +71,3 @@ """ | ||
| elif is_ast_node(node, (ast.FunctionDef, 'AsyncFunctionDef')) and node.name in node.namespace.nonlocal_names: | ||
| elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and node.name in node.namespace.nonlocal_names: | ||
| binding = get_binding_disallow_class_namespace_rename(node.name, node.namespace) | ||
@@ -97,11 +98,11 @@ binding.add_reference(node) | ||
| elif is_ast_node(node, 'Nonlocal'): | ||
| elif isinstance(node, ast.Nonlocal): | ||
| for name in node.names: | ||
| get_binding_disallow_class_namespace_rename(name, node.namespace).add_reference(node) | ||
| elif is_ast_node(node, ('MatchAs', 'MatchStar')) and node.name in node.namespace.nonlocal_names: | ||
| elif isinstance(node, (ast.MatchAs, ast.MatchStar)) and node.name in node.namespace.nonlocal_names: | ||
| get_binding_disallow_class_namespace_rename(node.name, node.namespace).add_reference(node) | ||
| elif is_ast_node(node, 'MatchMapping') and node.rest in node.namespace.nonlocal_names: | ||
| elif isinstance(node, ast.MatchMapping) and node.rest in node.namespace.nonlocal_names: | ||
| get_binding_disallow_class_namespace_rename(node.rest, node.namespace).add_reference(node) | ||
| elif is_ast_node(node, 'Exec'): | ||
| elif isinstance(node, ast.Exec): | ||
| get_global_namespace(node).tainted = True | ||
@@ -108,0 +109,0 @@ |
@@ -1,7 +0,8 @@ | ||
| import python_minifier.ast_compat as ast | ||
| import sys | ||
| from python_minifier.util import is_ast_node | ||
| import python_minifier.ast_compat as ast | ||
| from python_minifier.util import is_constant_node | ||
| def create_is_namespace(): | ||
@@ -35,2 +36,3 @@ | ||
| def get_global_namespace(node): | ||
@@ -129,3 +131,3 @@ """ | ||
| if (isinstance(node, ast.ImportFrom) and node.module == '__future__') or ( | ||
| isinstance(node, ast.Expr) and is_ast_node(node.value, ast.Str) | ||
| isinstance(node, ast.Expr) and is_constant_node(node.value, ast.Str) | ||
| ): | ||
@@ -169,3 +171,3 @@ pass | ||
| elif is_ast_node(node, (ast.AugAssign, 'AnnAssign')): | ||
| elif isinstance(node, (ast.AugAssign, ast.AnnAssign)): | ||
| if isinstance(node.target, ast.Name) and node.target.id == '__all__': | ||
@@ -184,3 +186,3 @@ return True | ||
| for el in node.value.elts: | ||
| if is_ast_node(el, ast.Str): | ||
| if is_constant_node(el, ast.Str): | ||
| names.append(el.s) | ||
@@ -187,0 +189,0 @@ |
@@ -6,2 +6,3 @@ """Tools for assembling python code from tokens.""" | ||
| class TokenTypes(object): | ||
@@ -19,2 +20,3 @@ NoToken = 0 | ||
| class Delimiter(object): | ||
@@ -79,2 +81,3 @@ def __init__(self, terminal_printer, delimiter=',', add_parens=False): | ||
| class TokenPrinter(object): | ||
@@ -81,0 +84,0 @@ """ |
@@ -33,3 +33,2 @@ import python_minifier.ast_compat as ast | ||
| def _combine_import_from(self, node_list, parent): | ||
@@ -36,0 +35,0 @@ |
@@ -1,10 +0,13 @@ | ||
| import python_minifier.ast_compat as ast | ||
| import math | ||
| import sys | ||
| import python_minifier.ast_compat as ast | ||
| from python_minifier.ast_annotation import get_parent | ||
| from python_minifier.ast_compare import compare_ast | ||
| from python_minifier.expression_printer import ExpressionPrinter | ||
| from python_minifier.transforms.suite_transformer import SuiteTransformer | ||
| from python_minifier.util import is_ast_node | ||
| from python_minifier.util import is_constant_node | ||
| class FoldConstants(SuiteTransformer): | ||
@@ -25,5 +28,5 @@ """ | ||
| # We don't try to fold strings or bytes, since they have probably been arranged this way to make the source shorter and we are unlikely to beat that | ||
| if not is_ast_node(node.left, (ast.Num, 'NameConstant')): | ||
| if not is_constant_node(node.left, (ast.Num, ast.NameConstant)): | ||
| return node | ||
| if not is_ast_node(node.right, (ast.Num, 'NameConstant')): | ||
| if not is_constant_node(node.right, (ast.Num, ast.NameConstant)): | ||
| return node | ||
@@ -72,3 +75,3 @@ | ||
| folded_value = safe_eval(folded_expression) | ||
| except Exception as e: | ||
| except Exception: | ||
| # This can happen if the value is too large to be represented as a literal | ||
@@ -96,4 +99,5 @@ # or if the value is unparsed as nan, inf or -inf - which are not valid python literals | ||
| # New representation is shorter and has the same value, so use it | ||
| return self.add_child(new_node, node.parent, node.namespace) | ||
| return self.add_child(new_node, get_parent(node), node.namespace) | ||
| def equal_value_and_type(a, b): | ||
@@ -108,11 +112,13 @@ if type(a) != type(b): | ||
| def safe_eval(expression): | ||
| globals = {} | ||
| locals = {} | ||
| empty_globals = {} | ||
| empty_locals = {} | ||
| # This will return the value, or could raise an exception | ||
| return eval(expression, globals, locals) | ||
| return eval(expression, empty_globals, empty_locals) | ||
| def unparse_expression(node): | ||
| expression_printer = ExpressionPrinter() | ||
| return expression_printer(node) | ||
| return expression_printer(node) |
@@ -29,3 +29,5 @@ class RemoveAnnotationsOptions(object): | ||
| def __repr__(self): | ||
| return 'RemoveAnnotationsOptions(remove_variable_annotations=%r, remove_return_annotations=%r, remove_argument_annotations=%r, remove_class_attribute_annotations=%r)' % (self.remove_variable_annotations, self.remove_return_annotations, self.remove_argument_annotations, self.remove_class_attribute_annotations) | ||
| return 'RemoveAnnotationsOptions(remove_variable_annotations=%r, remove_return_annotations=%r, remove_argument_annotations=%r, remove_class_attribute_annotations=%r)' % ( | ||
| self.remove_variable_annotations, self.remove_return_annotations, self.remove_argument_annotations, self.remove_class_attribute_annotations | ||
| ) | ||
@@ -32,0 +34,0 @@ def __nonzero__(self): |
| class RemoveAnnotationsOptions: | ||
| remove_variable_annotations: bool | ||
@@ -8,11 +7,13 @@ remove_return_annotations: bool | ||
| def __init__(self, | ||
| remove_variable_annotations: bool = ..., | ||
| remove_return_annotations: bool = ..., | ||
| remove_argument_annotations: bool = ..., | ||
| remove_class_attribute_annotations: bool = ...): | ||
| def __init__( | ||
| self, | ||
| remove_variable_annotations: bool = ..., | ||
| remove_return_annotations: bool = ..., | ||
| remove_argument_annotations: bool = ..., | ||
| remove_class_attribute_annotations: bool = ... | ||
| ): | ||
| ... | ||
| def __repr__(self) -> str: ... | ||
| def __nonzero__(self) -> bool: ... | ||
| def __bool__(self) -> bool: ... |
@@ -1,4 +0,6 @@ | ||
| import python_minifier.ast_compat as ast | ||
| import sys | ||
| import python_minifier.ast_compat as ast | ||
| from python_minifier.ast_annotation import get_parent | ||
| from python_minifier.transforms.remove_annotations_options import RemoveAnnotationsOptions | ||
@@ -74,16 +76,16 @@ from python_minifier.transforms.suite_transformer import SuiteTransformer | ||
| if not isinstance(node.parent, ast.ClassDef): | ||
| if not isinstance(get_parent(node), ast.ClassDef): | ||
| return False | ||
| if len(node.parent.decorator_list) == 0: | ||
| if len(get_parent(node).decorator_list) == 0: | ||
| return False | ||
| for node in node.parent.decorator_list: | ||
| if isinstance(node, ast.Name) and node.id == 'dataclass': | ||
| for decorator_node in get_parent(node).decorator_list: | ||
| if isinstance(decorator_node, ast.Name) and decorator_node.id == 'dataclass': | ||
| return True | ||
| elif isinstance(node, ast.Attribute) and node.attr == 'dataclass': | ||
| elif isinstance(decorator_node, ast.Attribute) and decorator_node.attr == 'dataclass': | ||
| return True | ||
| elif isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id == 'dataclass': | ||
| elif isinstance(decorator_node, ast.Call) and isinstance(decorator_node.func, ast.Name) and decorator_node.func.id == 'dataclass': | ||
| return True | ||
| elif isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute) and node.func.attr == 'dataclass': | ||
| elif isinstance(decorator_node, ast.Call) and isinstance(decorator_node.func, ast.Attribute) and decorator_node.func.attr == 'dataclass': | ||
| return True | ||
@@ -97,6 +99,6 @@ | ||
| if not isinstance(node.parent, ast.ClassDef): | ||
| if not isinstance(get_parent(node), ast.ClassDef): | ||
| return False | ||
| if len(node.parent.bases) == 0: | ||
| if len(get_parent(node).bases) == 0: | ||
| return False | ||
@@ -106,6 +108,6 @@ | ||
| for node in node.parent.bases: | ||
| if isinstance(node, ast.Name) and node.id in tricky_types: | ||
| for base_node in get_parent(node).bases: | ||
| if isinstance(base_node, ast.Name) and base_node.id in tricky_types: | ||
| return True | ||
| elif isinstance(node, ast. Attribute) and node.attr in tricky_types: | ||
| elif isinstance(base_node, ast.Attribute) and base_node.attr in tricky_types: | ||
| return True | ||
@@ -116,3 +118,3 @@ | ||
| # is this a class attribute or a variable? | ||
| if isinstance(node.parent, ast.ClassDef): | ||
| if isinstance(get_parent(node), ast.ClassDef): | ||
| if not self._options.remove_class_attribute_annotations: | ||
@@ -127,3 +129,3 @@ return node | ||
| elif node.value: | ||
| return self.add_child(ast.Assign([node.target], node.value), parent=node.parent, namespace=node.namespace) | ||
| return self.add_child(ast.Assign([node.target], node.value), parent=get_parent(node), namespace=node.namespace) | ||
| else: | ||
@@ -134,3 +136,3 @@ # Valueless annotations cause the interpreter to treat the variable as a local. | ||
| node.annotation = self.add_child(ast.Num(0), parent=node.parent, namespace=node.namespace) | ||
| node.annotation = self.add_child(ast.Num(0), parent=get_parent(node), namespace=node.namespace) | ||
| return node |
| import python_minifier.ast_compat as ast | ||
| from python_minifier.transforms.suite_transformer import SuiteTransformer | ||
| from python_minifier.util import is_ast_node | ||
@@ -18,3 +17,3 @@ | ||
| def suite(self, node_list, parent): | ||
| without_assert = [self.visit(a) for a in filter(lambda n: not is_ast_node(n, ast.Assert), node_list)] | ||
| without_assert = [self.visit(a) for a in filter(lambda n: not isinstance(n, ast.Assert), node_list)] | ||
@@ -21,0 +20,0 @@ if len(without_assert) == 0: |
@@ -1,6 +0,7 @@ | ||
| import python_minifier.ast_compat as ast | ||
| import sys | ||
| import python_minifier.ast_compat as ast | ||
| from python_minifier.transforms.suite_transformer import SuiteTransformer | ||
| from python_minifier.util import is_ast_node | ||
| from python_minifier.util import is_constant_node | ||
@@ -21,3 +22,3 @@ | ||
| return node.id == 'True' | ||
| elif is_ast_node(node, 'NameConstant'): | ||
| elif is_constant_node(node, ast.NameConstant): | ||
| return node.value | ||
@@ -24,0 +25,0 @@ return None |
@@ -11,7 +11,10 @@ """ | ||
| import python_minifier.ast_compat as ast | ||
| import sys | ||
| import python_minifier.ast_compat as ast | ||
| from python_minifier.ast_annotation import get_parent, set_parent | ||
| from python_minifier.rename.binding import BuiltinBinding | ||
| # These are always exceptions, in every version of python | ||
@@ -77,2 +80,3 @@ builtin_exceptions = [ | ||
| def _remove_empty_call(binding): | ||
@@ -83,13 +87,14 @@ assert isinstance(binding, BuiltinBinding) | ||
| # For this to be a builtin, all references must be name nodes as it is not defined anywhere | ||
| assert isinstance(name_node, ast.Name) and isinstance(name_node.ctx, ast.Load) | ||
| assert isinstance(name_node, ast.Name) | ||
| assert isinstance(name_node.ctx, ast.Load) | ||
| if not isinstance(name_node.parent, ast.Call): | ||
| if not isinstance(get_parent(name_node), ast.Call): | ||
| # This is not a call | ||
| continue | ||
| call_node = name_node.parent | ||
| call_node = get_parent(name_node) | ||
| if not isinstance(call_node.parent, ast.Raise): | ||
| if not isinstance(get_parent(call_node), ast.Raise): | ||
| # This is not a raise statement | ||
| continue | ||
| raise_node = call_node.parent | ||
| raise_node = get_parent(call_node) | ||
@@ -107,3 +112,3 @@ if len(call_node.args) > 0 or len(call_node.keywords) > 0: | ||
| raise_node.cause = name_node | ||
| name_node.parent = raise_node | ||
| set_parent(name_node, raise_node) | ||
@@ -110,0 +115,0 @@ |
@@ -1,6 +0,7 @@ | ||
| import python_minifier.ast_compat as ast | ||
| import sys | ||
| import python_minifier.ast_compat as ast | ||
| from python_minifier.transforms.suite_transformer import SuiteTransformer | ||
| from python_minifier.util import is_ast_node | ||
| from python_minifier.util import is_constant_node | ||
@@ -20,3 +21,3 @@ | ||
| elif sys.version_info >= (3, 4) and is_ast_node(node.value, 'NameConstant') and node.value.value is None: | ||
| elif sys.version_info >= (3, 4) and is_constant_node(node.value, ast.NameConstant) and node.value.value is None: | ||
| node.value = None | ||
@@ -27,3 +28,3 @@ | ||
| def visit_FunctionDef(self, node): | ||
| assert is_ast_node(node, (ast.FunctionDef, 'AsyncFunctionDef')) | ||
| assert isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) | ||
@@ -30,0 +31,0 @@ node.body = [self.visit(a) for a in node.body] |
| import python_minifier.ast_compat as ast | ||
| from python_minifier.transforms.suite_transformer import SuiteTransformer | ||
| from python_minifier.util import is_ast_node | ||
| from python_minifier.util import is_constant_node | ||
@@ -9,5 +9,4 @@ | ||
| if isinstance(node, ast.Attribute): | ||
| if node.attr == '__doc__': | ||
| raise ValueError('__doc__ found!') | ||
| if isinstance(node, ast.Attribute) and node.attr == '__doc__': | ||
| raise ValueError('__doc__ found!') | ||
@@ -22,3 +21,3 @@ for child in ast.iter_child_nodes(node): | ||
| return False | ||
| except: | ||
| except Exception: | ||
| return True | ||
@@ -52,3 +51,3 @@ | ||
| return is_ast_node(node.value, (ast.Num, ast.Str, 'NameConstant', 'Bytes')) | ||
| return is_constant_node(node.value, (ast.Num, ast.Str, ast.NameConstant, ast.Bytes)) | ||
@@ -55,0 +54,0 @@ def suite(self, node_list, parent): |
@@ -1,4 +0,5 @@ | ||
| import python_minifier.ast_compat as ast | ||
| import sys | ||
| import python_minifier.ast_compat as ast | ||
| from python_minifier.transforms.suite_transformer import SuiteTransformer | ||
@@ -5,0 +6,0 @@ |
| import python_minifier.ast_compat as ast | ||
| from python_minifier.transforms.suite_transformer import SuiteTransformer | ||
| from python_minifier.util import is_ast_node | ||
@@ -18,3 +17,3 @@ | ||
| def suite(self, node_list, parent): | ||
| without_pass = [self.visit(a) for a in filter(lambda n: not is_ast_node(n, ast.Pass), node_list)] | ||
| without_pass = [self.visit(a) for a in filter(lambda n: not isinstance(n, ast.Pass), node_list)] | ||
@@ -21,0 +20,0 @@ if len(without_pass) == 0: |
@@ -5,6 +5,5 @@ import python_minifier.ast_compat as ast | ||
| def remove_posargs(node): | ||
| if isinstance(node, ast.arguments): | ||
| if hasattr(node, 'posonlyargs'): | ||
| node.args = node.posonlyargs + node.args | ||
| node.posonlyargs = [] | ||
| if isinstance(node, ast.arguments) and hasattr(node, 'posonlyargs'): | ||
| node.args = node.posonlyargs + node.args | ||
| node.posonlyargs = [] | ||
@@ -11,0 +10,0 @@ for child in ast.iter_child_nodes(node): |
| import python_minifier.ast_compat as ast | ||
| from python_minifier.ast_annotation import get_parent, add_parent as add_node_parent | ||
| from python_minifier.rename.mapper import add_parent | ||
| from python_minifier.util import is_ast_node | ||
@@ -16,3 +16,3 @@ | ||
| """Called if no explicit visitor function exists for a node.""" | ||
| for field, value in ast.iter_fields(node): | ||
| for _field, value in ast.iter_fields(node): | ||
| if isinstance(value, list): | ||
@@ -189,5 +189,5 @@ for item in value: | ||
| if is_ast_node(node, (ast.FunctionDef, ast.Module, 'AsyncFunctionDef')): | ||
| if isinstance(node, (ast.FunctionDef, ast.Module, ast.AsyncFunctionDef)): | ||
| return node | ||
| return nearest_function_namespace(node.parent) | ||
| return nearest_function_namespace(get_parent(node)) | ||
@@ -197,3 +197,4 @@ if namespace is None: | ||
| add_parent(child, parent=parent, namespace=namespace) | ||
| add_node_parent(child, parent=parent) | ||
| add_parent(child, namespace=namespace) | ||
| return child |
| import python_minifier.ast_compat as ast | ||
| def is_ast_node(node, types): | ||
| def is_constant_node(node, types): | ||
| """ | ||
| Is a node one of the specified node types | ||
| A node type may be an actual ast class, or a string naming one. | ||
| types is a single node type or an iterable of many. | ||
| A node type may be an actual ast class or a tuple of many. | ||
| If a node_type specified a specific Constant type (Str, Bytes, Num etc), | ||
| If types includes a specific Constant type (Str, Bytes, Num etc), | ||
| returns true for Constant nodes of the correct type. | ||
@@ -22,25 +22,19 @@ | ||
| actual_types = [] | ||
| for node_type in types: | ||
| if isinstance(node_type, str): | ||
| node_type = getattr(ast, node_type, None) | ||
| if node_type is not None: | ||
| actual_types.append(node_type) | ||
| else: | ||
| actual_types.append(node_type) | ||
| assert not isinstance(node_type, str) | ||
| if isinstance(node, tuple(actual_types)): | ||
| if isinstance(node, types): | ||
| return True | ||
| if hasattr(ast, 'Constant') and isinstance(node, ast.Constant): | ||
| if isinstance(node, ast.Constant): | ||
| if type(node.value) in [type(None), type(True), type(False)]: | ||
| return ast.NameConstant in actual_types | ||
| return ast.NameConstant in types | ||
| elif isinstance(node.value, (int, float, complex)): | ||
| return ast.Num in actual_types | ||
| return ast.Num in types | ||
| elif isinstance(node.value, str): | ||
| return ast.Str in actual_types | ||
| return ast.Str in types | ||
| elif isinstance(node.value, bytes): | ||
| return ast.Bytes in actual_types | ||
| return ast.Bytes in types | ||
| elif node.value == Ellipsis: | ||
| return ast.Ellipsis in actual_types | ||
| return ast.Ellipsis in types | ||
| else: | ||
@@ -47,0 +41,0 @@ raise RuntimeError('Unknown Constant value %r' % type(node.value)) |
| import ast | ||
| import sys | ||
| import pytest | ||
| from python_minifier import unparse | ||
| from python_minifier.ast_compare import compare_ast | ||
| def test_pep(): | ||
@@ -15,5 +18,5 @@ if sys.version_info < (3, 8): | ||
| if self._is_special and (ans := self._check_nans(context=context)): | ||
| return ans | ||
| return ans | ||
| results = [(x, y, x/y) for x in input_data if (y := f(x)) > 0] | ||
| stuff = [[y := f(x), x/y] for x in range(5)] | ||
| stuff = [[y := f(x), x/y] for x in range(5)] | ||
| ''' | ||
@@ -20,0 +23,0 @@ |
| import ast | ||
| import sys | ||
| import pytest | ||
| from python_minifier import unparse | ||
| from python_minifier.ast_compare import compare_ast | ||
| def test_await_fstring(): | ||
@@ -8,0 +11,0 @@ if sys.version_info < (3, 7): |
@@ -7,2 +7,3 @@ import sys | ||
| def test_module_namespace(): | ||
@@ -9,0 +10,0 @@ if sys.version_info >= (3, 0): |
@@ -7,2 +7,3 @@ import sys | ||
| def test_class_typevar_default(): | ||
@@ -25,2 +26,3 @@ if sys.version_info < (3, 12): | ||
| def test_function_typevar_default(): | ||
@@ -43,2 +45,3 @@ if sys.version_info < (3, 12): | ||
| def test_alias_typevar_default(): | ||
@@ -61,2 +64,3 @@ if sys.version_info < (3, 12): | ||
| def test_class_typevartuple_default(): | ||
@@ -79,2 +83,3 @@ if sys.version_info < (3, 12): | ||
| def test_function_typevartuple_default(): | ||
@@ -97,2 +102,3 @@ if sys.version_info < (3, 12): | ||
| def test_alias_typevartuple_default(): | ||
@@ -115,2 +121,3 @@ if sys.version_info < (3, 12): | ||
| def test_class_paramspec_default(): | ||
@@ -133,2 +140,3 @@ if sys.version_info < (3, 12): | ||
| def test_function_paramspec_default(): | ||
@@ -151,2 +159,3 @@ if sys.version_info < (3, 12): | ||
| def test_alias_paramspec_default(): | ||
@@ -167,2 +176,2 @@ if sys.version_info < (3, 12): | ||
| assert_namespace_tree(source, expected_namespaces) | ||
| assert_namespace_tree(source, expected_namespaces) |
@@ -7,2 +7,3 @@ import sys | ||
| def test_class_typevar_default(): | ||
@@ -26,2 +27,3 @@ if sys.version_info < (3, 13): | ||
| def test_function_typevar_default(): | ||
@@ -47,2 +49,3 @@ if sys.version_info < (3, 13): | ||
| def test_alias_typevar_default(): | ||
@@ -68,2 +71,3 @@ if sys.version_info < (3, 13): | ||
| def test_class_typevartuple_default(): | ||
@@ -90,2 +94,3 @@ if sys.version_info < (3, 13): | ||
| def test_function_typevartuple_default(): | ||
@@ -113,2 +118,3 @@ if sys.version_info < (3, 13): | ||
| def test_alias_typevartuple_default(): | ||
@@ -134,2 +140,3 @@ if sys.version_info < (3, 13): | ||
| def test_class_paramspec_default(): | ||
@@ -156,2 +163,3 @@ if sys.version_info < (3, 13): | ||
| def test_function_paramspec_default(): | ||
@@ -179,2 +187,3 @@ if sys.version_info < (3, 13): | ||
| def test_alias_paramspec_default(): | ||
@@ -198,2 +207,2 @@ if sys.version_info < (3, 13): | ||
| assert_namespace_tree(source, expected_namespaces) | ||
| assert_namespace_tree(source, expected_namespaces) |
@@ -7,2 +7,3 @@ import sys | ||
| def test_module_namespace(): | ||
@@ -9,0 +10,0 @@ if sys.version_info < (3, 3) or sys.version_info > (3, 4): |
@@ -24,2 +24,3 @@ import sys | ||
| def test_lambda_namespace(): | ||
@@ -47,2 +48,3 @@ if sys.version_info < (3, 4): | ||
| def test_function_namespace(): | ||
@@ -81,2 +83,3 @@ if sys.version_info < (3, 4): | ||
| def test_async_function_namespace(): | ||
@@ -115,2 +118,3 @@ if sys.version_info < (3, 5): | ||
| # region generator namespace | ||
@@ -136,2 +140,3 @@ | ||
| def test_multi_generator_namespace(): | ||
@@ -158,2 +163,3 @@ if sys.version_info < (3, 4): | ||
| def test_multi_generator_namespace_2(): | ||
@@ -183,2 +189,3 @@ if sys.version_info < (3, 4): | ||
| def test_nested_generator(): | ||
@@ -209,2 +216,3 @@ if sys.version_info < (3, 4): | ||
| def test_nested_generator_2(): | ||
@@ -231,2 +239,3 @@ if sys.version_info < (3, 4): | ||
| # endregion | ||
@@ -255,2 +264,3 @@ | ||
| def test_multi_setcomp_namespace(): | ||
@@ -277,2 +287,3 @@ if sys.version_info < (3, 4): | ||
| def test_multi_setcomp_namespace_2(): | ||
@@ -302,2 +313,3 @@ if sys.version_info < (3, 4): | ||
| def test_nested_setcomp(): | ||
@@ -328,2 +340,3 @@ if sys.version_info < (3, 4): | ||
| def test_nested_setcomp_2(): | ||
@@ -350,2 +363,3 @@ if sys.version_info < (3, 4): | ||
| # endregion | ||
@@ -373,2 +387,3 @@ | ||
| def test_multi_listcomp_namespace(): | ||
@@ -395,2 +410,3 @@ if sys.version_info < (3, 4): | ||
| def test_multi_listcomp_namespace_2(): | ||
@@ -420,2 +436,3 @@ if sys.version_info < (3, 4): | ||
| def test_nested_listcomp(): | ||
@@ -446,2 +463,3 @@ if sys.version_info < (3, 4): | ||
| def test_nested_listcomp_2(): | ||
@@ -468,2 +486,3 @@ if sys.version_info < (3, 4): | ||
| # endregion | ||
@@ -491,2 +510,3 @@ | ||
| def test_multi_dictcomp_namespace(): | ||
@@ -513,2 +533,3 @@ if sys.version_info < (3, 4): | ||
| def test_multi_dictcomp_namespace_2(): | ||
@@ -538,2 +559,3 @@ if sys.version_info < (3, 4): | ||
| def test_nested_dictcomp(): | ||
@@ -564,2 +586,3 @@ if sys.version_info < (3, 4): | ||
| def test_nested_dictcomp_2(): | ||
@@ -586,2 +609,3 @@ if sys.version_info < (3, 4): | ||
| # endregion | ||
@@ -638,4 +662,4 @@ | ||
| MyOtherName = OhALongName | ||
| func() | ||
@@ -658,2 +682,3 @@ ''' | ||
| def test_class_namespace_nonlocal_name_allow_rename(): | ||
@@ -679,2 +704,3 @@ source = ''' | ||
| def test_class_namespace_undefined_nonlocal_name_disallow_rename(): | ||
@@ -697,2 +723,3 @@ source = ''' | ||
| def test_class_namespace_nonlocal_builtin_name_allow_rename(): | ||
@@ -716,2 +743,3 @@ source = ''' | ||
| def test_class_namespace_nonlocal_builtin_name_disallow_rename(): | ||
@@ -759,2 +787,3 @@ source = ''' | ||
| def test_class_namespace_nonlocal_name_disallow_rename(): | ||
@@ -781,3 +810,4 @@ source = ''' | ||
| def test_class_namespace_undefined_nonlocal_name_disallow_rename(): | ||
| def test_class_namespace_undefined_nonlocal_name_disallow_rename_2(): | ||
| source = ''' | ||
@@ -800,2 +830,3 @@ class LazyList: | ||
| def test_class_namespace_nonlocal_import_as_disallow_rename(): | ||
@@ -823,2 +854,3 @@ """import os as MyNonLocal""" | ||
| def test_class_namespace_undefined_nonlocal_import_as_disallow_rename(): | ||
@@ -843,2 +875,3 @@ source = ''' | ||
| def test_class_namespace_nonlocal_import_from_as_disallow_rename(): | ||
@@ -865,2 +898,3 @@ source = ''' | ||
| def test_class_namespace_undefined_nonlocal_import_from_as_disallow_rename(): | ||
@@ -885,2 +919,3 @@ source = ''' | ||
| def test_class_namespace_nonlocal_import_disallow_rename(): | ||
@@ -907,2 +942,3 @@ source = ''' | ||
| def test_class_namespace_undefined_nonlocal_import_disallow_rename(): | ||
@@ -927,2 +963,3 @@ source = ''' | ||
| def test_class_namespace_nonlocal_import_from_disallow_rename(): | ||
@@ -949,2 +986,3 @@ source = ''' | ||
| def test_class_namespace_undefined_nonlocal_import_from_disallow_rename(): | ||
@@ -969,2 +1007,3 @@ source = ''' | ||
| def test_class_namespace_nonlocal_dotted_import_disallow_rename(): | ||
@@ -991,2 +1030,3 @@ source = ''' | ||
| def test_class_namespace_undefined_nonlocal_dotted_import_disallow_rename(): | ||
@@ -1011,2 +1051,3 @@ source = ''' | ||
| def test_class_namespace_nonlocal_func_disallow_rename(): | ||
@@ -1034,2 +1075,3 @@ source = ''' | ||
| def test_class_namespace_undefined_nonlocal_func_disallow_rename(): | ||
@@ -1055,2 +1097,3 @@ source = ''' | ||
| def test_class_namespace_nonlocal_async_func_disallow_rename(): | ||
@@ -1081,2 +1124,3 @@ if sys.version_info < (3, 5): | ||
| def test_class_namespace_undefined_nonlocal_async_func_disallow_rename(): | ||
@@ -1105,2 +1149,3 @@ if sys.version_info < (3, 5): | ||
| def test_class_namespace_nonlocal_classdef_disallow_rename(): | ||
@@ -1128,2 +1173,3 @@ source = ''' | ||
| def test_class_namespace_undefined_nonlocal_classdef_disallow_rename(): | ||
@@ -1176,2 +1222,3 @@ source = ''' | ||
| def test_class_namespace_undefined_nonlocal_except_disallow_rename(): | ||
@@ -1184,3 +1231,3 @@ source = ''' | ||
| except Exception as MyNonLocal: | ||
| pass | ||
| pass | ||
@@ -1210,3 +1257,3 @@ ''' | ||
| Blah = "Hello" | ||
| class LazyList: | ||
@@ -1232,2 +1279,3 @@ MyAttribute = MyNonLocal | ||
| def test_class_namespace_undefined_match_disallow_rename(): | ||
@@ -1243,3 +1291,3 @@ if sys.version_info < (3, 10): | ||
| case MyNonLocal: | ||
| pass | ||
| pass | ||
| ''' | ||
@@ -1260,2 +1308,3 @@ | ||
| def test_class_namespace_nonlocal_match_star_disallow_rename(): | ||
@@ -1289,2 +1338,3 @@ if sys.version_info < (3, 10): | ||
| def test_class_namespace_undefined_match_star_disallow_rename(): | ||
@@ -1300,3 +1350,3 @@ if sys.version_info < (3, 10): | ||
| case [*MyNonLocal]: | ||
| pass | ||
| pass | ||
| ''' | ||
@@ -1317,2 +1367,3 @@ | ||
| def test_class_namespace_nonlocal_match_mapping_disallow_rename(): | ||
@@ -1346,2 +1397,3 @@ if sys.version_info < (3, 10): | ||
| def test_class_namespace_undefined_match_mapping_disallow_rename(): | ||
@@ -1357,3 +1409,3 @@ if sys.version_info < (3, 10): | ||
| case {**MyNonLocal}: | ||
| pass | ||
| pass | ||
| ''' | ||
@@ -1374,2 +1426,3 @@ | ||
| def test_class_namespace_nonlocal_disallow_rename(): | ||
@@ -1396,2 +1449,3 @@ if sys.version_info < (3, 0): | ||
| def test_class_namespace_undefined_nonlocal_disallow_rename(): | ||
@@ -1398,0 +1452,0 @@ if sys.version_info < (3, 0): |
| 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.ast_annotation import add_parent | ||
| from python_minifier.ast_compare import compare_ast | ||
| from python_minifier.rename import add_namespace, bind_names, resolve_names | ||
| from python_minifier.transforms.combine_imports import CombineImports | ||
| from python_minifier.ast_compare import compare_ast | ||
| def combine_imports(module): | ||
| add_parent(module) | ||
| add_namespace(module) | ||
@@ -14,5 +17,7 @@ CombineImports()(module) | ||
| def assert_namespace_tree(source, expected_tree): | ||
| tree = ast.parse(source) | ||
| add_parent(tree) | ||
| add_namespace(tree) | ||
@@ -28,2 +33,3 @@ CombineImports()(tree) | ||
| def test_import(): | ||
@@ -34,3 +40,2 @@ source = '''import builtins | ||
| expected_ast = ast.parse(expected) | ||
@@ -40,2 +45,3 @@ actual_ast = combine_imports(ast.parse(source)) | ||
| def test_import_as(): | ||
@@ -51,3 +57,2 @@ source = '''import builtins | ||
| expected_ast = ast.parse(expected) | ||
@@ -63,3 +68,3 @@ actual_ast = combine_imports(ast.parse(source)) | ||
| from collections import abc''' | ||
| expected = '''from builtins import dir, help | ||
| expected = '''from builtins import dir, help | ||
| import collections | ||
@@ -72,2 +77,3 @@ from collections import abc''' | ||
| def test_import_in_function(): | ||
@@ -77,3 +83,3 @@ source = '''def test(): | ||
| import builtins | ||
| return None | ||
@@ -108,2 +114,3 @@ ''' | ||
| def test_import_as_class_namespace(): | ||
@@ -125,2 +132,3 @@ | ||
| def test_import_class_namespace(): | ||
@@ -142,2 +150,3 @@ | ||
| def test_from_import_class_namespace(): | ||
@@ -159,2 +168,3 @@ | ||
| def test_from_import_as_class_namespace(): | ||
@@ -175,2 +185,1 @@ | ||
| assert_namespace_tree(source, expected_namespaces) | ||
@@ -9,5 +9,6 @@ import ast | ||
| def test_listcomp_regression_2_7(): | ||
| if sys.version_info >= (3, 0): | ||
| pytest.skip('ListComp doesn\'t create a new namespace in python < 3.0') | ||
| pytest.skip("ListComp doesn't create a new namespace in python < 3.0") | ||
@@ -21,2 +22,3 @@ source = ''' | ||
| def test_listcomp_regression(): | ||
@@ -47,2 +49,3 @@ if sys.version_info < (3, 0): | ||
| def test_generator_expression(): | ||
@@ -63,2 +66,3 @@ source = ''' | ||
| def test_generator_expression_multiple_for(): | ||
@@ -83,2 +87,3 @@ source = ''' | ||
| def test_generator_expression_nested_for(): | ||
@@ -85,0 +90,0 @@ source = ''' |
| import ast | ||
| import sys | ||
| import pytest | ||
| from python_minifier import unparse | ||
| from python_minifier.ast_compare import compare_ast | ||
| def test_pep(): | ||
@@ -11,3 +14,3 @@ if sys.version_info < (3, 9): | ||
| source = """ | ||
| source = ''' | ||
| buttons = [QPushButton(f'Button {i}') for i in range(10)] | ||
@@ -59,3 +62,3 @@ | ||
| """ | ||
| ''' | ||
@@ -62,0 +65,0 @@ expected_ast = ast.parse(source) |
| import ast | ||
| import sys | ||
| import pytest | ||
| from python_minifier import unparse | ||
| from python_minifier.ast_compare import compare_ast | ||
| def test_dict_expanson(): | ||
@@ -8,0 +11,0 @@ if sys.version_info < (3, 5): |
+97
-82
@@ -6,5 +6,6 @@ import ast | ||
| from python_minifier import add_namespace | ||
| from python_minifier.ast_annotation import add_parent | ||
| from python_minifier.ast_compare import compare_ast | ||
| from python_minifier.rename import add_namespace | ||
| from python_minifier.transforms.constant_folding import FoldConstants | ||
| from python_minifier.ast_compare import compare_ast | ||
@@ -14,2 +15,3 @@ | ||
| module = ast.parse(source) | ||
| add_parent(module) | ||
| add_namespace(module) | ||
@@ -19,2 +21,3 @@ FoldConstants()(module) | ||
| def run_test(source, expected): | ||
@@ -29,48 +32,51 @@ try: | ||
| @pytest.mark.parametrize('source,expected', [ | ||
| ('True | True', 'True'), | ||
| ('True | False', 'True'), | ||
| ('False | True', 'True'), | ||
| ('False | False', 'False'), | ||
| ('True & True', 'True'), | ||
| ('True & False', 'False'), | ||
| ('False & True', 'False'), | ||
| ('False & False', 'False'), | ||
| ('True ^ True', 'False'), | ||
| ('True ^ False', 'True'), | ||
| ('False ^ True', 'True'), | ||
| ('False ^ False', 'False'), | ||
| ('(True | True) | True', 'True'), | ||
| ('(True | True) | False', 'True'), | ||
| ('(True | False) | True', 'True'), | ||
| ('(True | False) | False', 'True'), | ||
| ('(False | True) | True', 'True'), | ||
| ('(False | True) | False', 'True'), | ||
| ('(False | False) | True', 'True'), | ||
| ('(False | False) | False', 'False'), | ||
| ('(True | True) & True', 'True'), | ||
| ('(True | True) & False', 'False'), | ||
| ('(True | False) & True', 'True'), | ||
| ('(True | False) & False', 'False'), | ||
| ('(False | True) & True', 'True'), | ||
| ('(False | True) & False', 'False'), | ||
| ('(False | False) & True', 'False'), | ||
| ('(False | False) & False', 'False'), | ||
| ('(True | True) ^ True', 'False'), | ||
| ('(True | True) ^ False', 'True'), | ||
| ('(True | False) ^ True', 'False'), | ||
| ('(True | False) ^ False', 'True'), | ||
| ('(False | True) ^ True', 'False'), | ||
| ('(False | True) ^ False', 'True'), | ||
| ('(False | False) ^ True', 'True'), | ||
| ('(False | False) ^ False', 'False'), | ||
| ('True | (True | True)', 'True'), | ||
| ('True | (True | False)', 'True'), | ||
| ('True | (False | True)', 'True'), | ||
| ('True | (False | False)', 'True'), | ||
| ('False | (True | True)', 'True'), | ||
| ('False | (True | False)', 'True'), | ||
| ('False | (False | True)', 'True'), | ||
| ('False | (False | False)', 'False'), | ||
| ]) | ||
| @pytest.mark.parametrize( | ||
| ('source', 'expected'), [ | ||
| ('True | True', 'True'), | ||
| ('True | False', 'True'), | ||
| ('False | True', 'True'), | ||
| ('False | False', 'False'), | ||
| ('True & True', 'True'), | ||
| ('True & False', 'False'), | ||
| ('False & True', 'False'), | ||
| ('False & False', 'False'), | ||
| ('True ^ True', 'False'), | ||
| ('True ^ False', 'True'), | ||
| ('False ^ True', 'True'), | ||
| ('False ^ False', 'False'), | ||
| ('(True | True) | True', 'True'), | ||
| ('(True | True) | False', 'True'), | ||
| ('(True | False) | True', 'True'), | ||
| ('(True | False) | False', 'True'), | ||
| ('(False | True) | True', 'True'), | ||
| ('(False | True) | False', 'True'), | ||
| ('(False | False) | True', 'True'), | ||
| ('(False | False) | False', 'False'), | ||
| ('(True | True) & True', 'True'), | ||
| ('(True | True) & False', 'False'), | ||
| ('(True | False) & True', 'True'), | ||
| ('(True | False) & False', 'False'), | ||
| ('(False | True) & True', 'True'), | ||
| ('(False | True) & False', 'False'), | ||
| ('(False | False) & True', 'False'), | ||
| ('(False | False) & False', 'False'), | ||
| ('(True | True) ^ True', 'False'), | ||
| ('(True | True) ^ False', 'True'), | ||
| ('(True | False) ^ True', 'False'), | ||
| ('(True | False) ^ False', 'True'), | ||
| ('(False | True) ^ True', 'False'), | ||
| ('(False | True) ^ False', 'True'), | ||
| ('(False | False) ^ True', 'True'), | ||
| ('(False | False) ^ False', 'False'), | ||
| ('True | (True | True)', 'True'), | ||
| ('True | (True | False)', 'True'), | ||
| ('True | (False | True)', 'True'), | ||
| ('True | (False | False)', 'True'), | ||
| ('False | (True | True)', 'True'), | ||
| ('False | (True | False)', 'True'), | ||
| ('False | (False | True)', 'True'), | ||
| ('False | (False | False)', 'False'), | ||
| ] | ||
| ) | ||
| def test_bool(source, expected): | ||
@@ -88,21 +94,24 @@ """ | ||
| @pytest.mark.parametrize('source,expected', [ | ||
| ('10 + 10', '20'), | ||
| ('10 + 0', '10'), | ||
| ('0 + 10', '10'), | ||
| ('10 + 10 + 5', '25'), | ||
| ('10 - 5 + 5', '10'), | ||
| ('10 * 10', '100'), | ||
| ('10 * 10 * 10', '1000'), | ||
| ('(10 * 10) // 10', '10'), | ||
| ('(2 * 10) // (2+2)', '5'), | ||
| ('8>>2', '2'), | ||
| ('8<<2', '32'), | ||
| ('0xff^0x0f', '0xf0'), | ||
| ('0xf0&0xff', '0xf0'), | ||
| ('0xf0|0x0f', '0xff'), | ||
| ('10%2', '0'), | ||
| ('10%3', '1'), | ||
| ('10-100', '-90') | ||
| ]) | ||
| @pytest.mark.parametrize( | ||
| ('source', 'expected'), [ | ||
| ('10 + 10', '20'), | ||
| ('10 + 0', '10'), | ||
| ('0 + 10', '10'), | ||
| ('10 + 10 + 5', '25'), | ||
| ('10 - 5 + 5', '10'), | ||
| ('10 * 10', '100'), | ||
| ('10 * 10 * 10', '1000'), | ||
| ('(10 * 10) // 10', '10'), | ||
| ('(2 * 10) // (2+2)', '5'), | ||
| ('8>>2', '2'), | ||
| ('8<<2', '32'), | ||
| ('0xff^0x0f', '0xf0'), | ||
| ('0xf0&0xff', '0xf0'), | ||
| ('0xf0|0x0f', '0xff'), | ||
| ('10%2', '0'), | ||
| ('10%3', '1'), | ||
| ('10-100', '-90') | ||
| ] | ||
| ) | ||
| def test_int(source, expected): | ||
@@ -115,10 +124,13 @@ """ | ||
| @pytest.mark.parametrize('source,expected', [ | ||
| ('10/10', '10/10'), | ||
| ('5+5/10', '5+5/10'), | ||
| ('2*5/10', '10/10'), | ||
| ('2/5*10', '2/5*10'), | ||
| ('2**5', '2**5'), | ||
| ('5@6', '5@6'), | ||
| ]) | ||
| @pytest.mark.parametrize( | ||
| ('source', 'expected'), [ | ||
| ('10/10', '10/10'), | ||
| ('5+5/10', '5+5/10'), | ||
| ('2*5/10', '10/10'), | ||
| ('2/5*10', '2/5*10'), | ||
| ('2**5', '2**5'), | ||
| ('5@6', '5@6'), | ||
| ] | ||
| ) | ||
| def test_int_not_eval(source, expected): | ||
@@ -131,8 +143,11 @@ """ | ||
| @pytest.mark.parametrize('source,expected', [ | ||
| ('"Hello" + "World"', '"Hello" + "World"'), | ||
| ('"Hello" * 5', '"Hello" * 5'), | ||
| ('b"Hello" + b"World"', 'b"Hello" + b"World"'), | ||
| ('b"Hello" * 5', 'b"Hello" * 5'), | ||
| ]) | ||
| @pytest.mark.parametrize( | ||
| ('source', 'expected'), [ | ||
| ('"Hello" + "World"', '"Hello" + "World"'), | ||
| ('"Hello" * 5', '"Hello" * 5'), | ||
| ('b"Hello" + b"World"', 'b"Hello" + b"World"'), | ||
| ('b"Hello" * 5', 'b"Hello" * 5'), | ||
| ] | ||
| ) | ||
| def test_not_eval(source, expected): | ||
@@ -143,2 +158,2 @@ """ | ||
| run_test(source, expected) | ||
| run_test(source, expected) |
+19
-16
@@ -10,12 +10,13 @@ import ast | ||
| @pytest.mark.parametrize('statement', [ | ||
| 'f"{1=!r:.4}"', | ||
| 'f"{1=:.4}"', | ||
| 'f"{1=!s:.4}"', | ||
| 'f"{1=:.4}"', | ||
| 'f"{1}"', | ||
| 'f"{1=}"', | ||
| 'f"{1=!s}"', | ||
| 'f"{1=!a}"' | ||
| ]) | ||
| @pytest.mark.parametrize( | ||
| 'statement', [ | ||
| 'f"{1=!r:.4}"', | ||
| 'f"{1=:.4}"', | ||
| 'f"{1=!s:.4}"', | ||
| 'f"{1}"', | ||
| 'f"{1=}"', | ||
| 'f"{1=!s}"', | ||
| 'f"{1=!a}"' | ||
| ] | ||
| ) | ||
| def test_fstring_statement(statement): | ||
@@ -27,2 +28,3 @@ if sys.version_info < (3, 8): | ||
| def test_pep0701(): | ||
@@ -41,3 +43,3 @@ if sys.version_info < (3, 12): | ||
| statement = """ | ||
| statement = ''' | ||
| f"This is the playlist: {", ".join([ | ||
@@ -48,7 +50,7 @@ 'Take me back to Eden', # My, my, those eyes like fire | ||
| ])}" | ||
| """ | ||
| ''' | ||
| assert unparse(ast.parse(statement)) == 'f"This is the playlist: {", ".join(["Take me back to Eden","Alkaline","Ascensionism"])}"' | ||
| #statement = '''print(f"This is the playlist: {"\N{BLACK HEART SUIT}".join(songs)}")''' | ||
| #assert unparse(ast.parse(statement)) == statement | ||
| # statement = '''print(f"This is the playlist: {"\N{BLACK HEART SUIT}".join(songs)}")''' | ||
| # assert unparse(ast.parse(statement)) == statement | ||
@@ -83,4 +85,4 @@ statement = '''f"Magic wand: {bag["wand"]}"''' | ||
| #statement = '''f"{"":*^{1:{1:{1}}}}"''' | ||
| #assert unparse(ast.parse(statement)) == statement | ||
| # statement = '''f"{"":*^{1:{1:{1}}}}"''' | ||
| # assert unparse(ast.parse(statement)) == statement | ||
| # SyntaxError: f-string: expressions nested too deeply | ||
@@ -96,2 +98,3 @@ | ||
| def test_fstring_empty_str(): | ||
@@ -98,0 +101,0 @@ if sys.version_info < (3, 6): |
| import ast | ||
| import sys | ||
| import pytest | ||
| from python_minifier import unparse | ||
| from python_minifier.ast_compare import compare_ast | ||
| def test_type_statement(): | ||
@@ -11,3 +14,2 @@ if sys.version_info < (3, 12): | ||
| source = ''' | ||
@@ -22,2 +24,3 @@ type Point = tuple[float, float] | ||
| def test_function_generic(): | ||
@@ -37,2 +40,3 @@ if sys.version_info < (3, 12): | ||
| def test_class_generic(): | ||
@@ -281,2 +285,1 @@ if sys.version_info < (3, 12): | ||
| unparse(expected_ast) | ||
@@ -7,13 +7,24 @@ import ast | ||
| from python_minifier import unparse | ||
| from python_minifier.ast_annotation import add_parent | ||
| from python_minifier.ast_compare import compare_ast | ||
| from python_minifier.ast_printer import print_ast | ||
| from python_minifier.rename import add_namespace, bind_names, resolve_names, rename, rename_literals, allow_rename_locals, allow_rename_globals | ||
| from python_minifier.rename import ( | ||
| add_namespace, | ||
| allow_rename_globals, | ||
| allow_rename_locals, | ||
| bind_names, | ||
| rename, | ||
| rename_literals, | ||
| resolve_names | ||
| ) | ||
| def hoist(source): | ||
| module = ast.parse(source) | ||
| add_parent(module) | ||
| add_namespace(module) | ||
| bind_names(module) | ||
| resolve_names(module) | ||
| allow_rename_locals(module, False) | ||
| allow_rename_globals(module, False) | ||
| allow_rename_locals(module, rename_locals=False) | ||
| allow_rename_globals(module, rename_globals=False) | ||
| rename_literals(module) | ||
@@ -24,2 +35,3 @@ rename(module) | ||
| def test_nohoist_single_usage(): | ||
@@ -38,2 +50,3 @@ source = ''' | ||
| def test_hoist_multiple_usage(): | ||
@@ -55,2 +68,3 @@ source = ''' | ||
| def test_no_hoist_multiple_small(): | ||
@@ -71,2 +85,3 @@ source = ''' | ||
| def test_hoist_multiple_small(): | ||
@@ -98,2 +113,3 @@ source = ''' | ||
| def test_hoist_insert_after_docstring(): | ||
@@ -140,2 +156,3 @@ source = ''' | ||
| def test_hoist_insert_after_future_docstring(): | ||
@@ -163,2 +180,3 @@ source = ''' | ||
| def test_hoist_bytes(): | ||
@@ -186,2 +204,3 @@ source = ''' | ||
| def test_hoist_after_multiple_future(): | ||
@@ -211,2 +230,3 @@ source = ''' | ||
| def test_hoist_class(): | ||
@@ -230,2 +250,3 @@ source = ''' | ||
| def test_hoist_from_class_to_func(): | ||
@@ -251,2 +272,3 @@ source = ''' | ||
| def test_hoist_from_func_to_func(): | ||
@@ -274,2 +296,3 @@ source = ''' | ||
| def test_hoist_in_func(): | ||
@@ -295,2 +318,3 @@ source = ''' | ||
| def test_hoist_over_class(): | ||
@@ -318,2 +342,3 @@ source = ''' | ||
| def test_hoist_from_generator(): | ||
@@ -326,3 +351,3 @@ source = ''' | ||
| for i in (a for a in 'World'): | ||
| pass | ||
| pass | ||
| return 'World' | ||
@@ -341,3 +366,3 @@ def c(): | ||
| for i in (a for a in B): | ||
| pass | ||
| pass | ||
| return B | ||
@@ -352,2 +377,3 @@ def c(): | ||
| def test_hoist_from_listcomp(): | ||
@@ -360,3 +386,3 @@ source = ''' | ||
| for i in [a for a in 'World']: | ||
| pass | ||
| pass | ||
| return 'World' | ||
@@ -375,3 +401,3 @@ def c(): | ||
| for i in [a for a in B]: | ||
| pass | ||
| pass | ||
| return B | ||
@@ -397,3 +423,3 @@ def c(): | ||
| for i in {a: a for a in 'World'}: | ||
| pass | ||
| pass | ||
| return 'World' | ||
@@ -412,3 +438,3 @@ def c(): | ||
| for i in {a: a for a in B}: | ||
| pass | ||
| pass | ||
| return B | ||
@@ -423,2 +449,3 @@ def c(): | ||
| def test_hoist_from_setcomp(): | ||
@@ -433,3 +460,3 @@ if sys.version_info < (2, 7): | ||
| pass | ||
| return 'World' | ||
| return 'World' | ||
| def c(): | ||
@@ -445,3 +472,3 @@ return 'World' | ||
| for i in {a for a in B + B + A}: | ||
| pass | ||
| pass | ||
| return A | ||
@@ -456,2 +483,3 @@ def c(): | ||
| def test_hoist_from_lambda(): | ||
@@ -479,2 +507,3 @@ source = ''' | ||
| def test_hoisted_types_py3(): | ||
@@ -523,2 +552,3 @@ if sys.version_info < (3, 0): | ||
| def test_no_hoist_slots(): | ||
@@ -525,0 +555,0 @@ source = ''' |
+23
-21
@@ -8,24 +8,26 @@ import ast | ||
| @pytest.mark.parametrize('statement', [ | ||
| 'import a', | ||
| 'import a,b', | ||
| 'import a as b', | ||
| 'import a as b,c as d', | ||
| 'import a.b', | ||
| 'import a.b.c', | ||
| 'import a.b.c as d', | ||
| 'import a.b.c as d,e as f', | ||
| 'from a import A', | ||
| 'from.import A', | ||
| 'from.import*', | ||
| 'from..import A,B', | ||
| 'from.a import A', | ||
| 'from...a import A', | ||
| 'from...a import*', | ||
| 'from a import A as B', | ||
| 'from a import A as B,C as D', | ||
| 'from.a.b import A', | ||
| 'from....a.b.c import A', | ||
| ]) | ||
| @pytest.mark.parametrize( | ||
| 'statement', [ | ||
| 'import a', | ||
| 'import a,b', | ||
| 'import a as b', | ||
| 'import a as b,c as d', | ||
| 'import a.b', | ||
| 'import a.b.c', | ||
| 'import a.b.c as d', | ||
| 'import a.b.c as d,e as f', | ||
| 'from a import A', | ||
| 'from.import A', | ||
| 'from.import*', | ||
| 'from..import A,B', | ||
| 'from.a import A', | ||
| 'from...a import A', | ||
| 'from...a import*', | ||
| 'from a import A as B', | ||
| 'from a import A as B,C as D', | ||
| 'from.a.b import A', | ||
| 'from....a.b.c import A', | ||
| ] | ||
| ) | ||
| def test_import_statement(statement): | ||
| assert unparse(ast.parse(statement)) == statement |
| import ast | ||
| import sys | ||
| import pytest | ||
| from python_minifier import unparse | ||
| from python_minifier.ast_compare import compare_ast | ||
| def test_return(): | ||
@@ -8,0 +11,0 @@ if sys.version_info < (3, 0): |
@@ -6,11 +6,20 @@ import ast | ||
| from python_minifier import ( | ||
| add_namespace, bind_names, resolve_names, allow_rename_locals, allow_rename_globals, | ||
| compare_ast, rename as do_rename, CompareError, unparse, rename_literals | ||
| from python_minifier import unparse | ||
| from python_minifier.ast_annotation import add_parent | ||
| from python_minifier.ast_compare import CompareError, compare_ast | ||
| from python_minifier.rename import ( | ||
| add_namespace, | ||
| allow_rename_globals, | ||
| allow_rename_locals, | ||
| bind_names, | ||
| rename, | ||
| rename_literals, | ||
| resolve_names | ||
| ) | ||
| def rename(source, locals, globals): | ||
| def do_rename(source, allow_locals, allow_globals): | ||
| # This will raise if the source file can't be parsed | ||
| module = ast.parse(source, 'test_match_rename') | ||
| add_parent(module) | ||
| add_namespace(module) | ||
@@ -20,7 +29,7 @@ bind_names(module) | ||
| allow_rename_locals(module, locals) | ||
| allow_rename_globals(module, globals) | ||
| allow_rename_locals(module, allow_locals) | ||
| allow_rename_globals(module, allow_globals) | ||
| rename_literals(module) | ||
| do_rename(module) | ||
| rename(module) | ||
@@ -50,5 +59,5 @@ return module | ||
| case hello: pass | ||
| match 'hello' + 'hello': | ||
| case 'hello': pass | ||
| case 'hello': pass | ||
| ''' | ||
@@ -65,7 +74,7 @@ expected = ''' | ||
| match B + B: | ||
| case 'hello': pass | ||
| case 'hello': pass | ||
| ''' | ||
| expected_ast = ast.parse(expected) | ||
| actual_ast = rename(source, locals=False, globals=True) | ||
| actual_ast = do_rename(source, allow_locals=False, allow_globals=True) | ||
| assert_code(expected_ast, actual_ast) | ||
@@ -88,3 +97,3 @@ | ||
| case None: pass | ||
| match 'hello' + 'hello' + 'hello': | ||
@@ -104,9 +113,9 @@ case 'hello': pass | ||
| case None: pass | ||
| match B + B + B: | ||
| case 'hello': pass | ||
| case 'hello': pass | ||
| ''' | ||
| expected_ast = ast.parse(expected) | ||
| actual_ast = rename(source, locals=True, globals=False) | ||
| actual_ast = do_rename(source, allow_locals=True, allow_globals=False) | ||
| assert_code(expected_ast, actual_ast) | ||
@@ -127,4 +136,4 @@ | ||
| case None if hello + hello + hello + hello: pass | ||
| case None if expensive_rename: pass | ||
| case 'hello' if 'hello' + 'hello' + 'hello' + 'hello': pass | ||
| case None if expensive_rename: pass | ||
| case 'hello' if 'hello' + 'hello' + 'hello' + 'hello': pass | ||
| ''' | ||
@@ -141,7 +150,7 @@ expected = ''' | ||
| case None if expensive_rename: pass | ||
| case 'hello' if B + B + B + B: pass | ||
| case 'hello' if B + B + B + B: pass | ||
| ''' | ||
| expected_ast = ast.parse(expected) | ||
| actual_ast = rename(source, locals=True, globals=False) | ||
| actual_ast = do_rename(source, allow_locals=True, allow_globals=False) | ||
| assert_code(expected_ast, actual_ast) | ||
@@ -158,3 +167,3 @@ | ||
| case None if sausage + sausage + sausage + sausage: pass | ||
| case 'hello' if 'hello' + 'hello' + 'hello' + 'hello': pass | ||
| case 'hello' if 'hello' + 'hello' + 'hello' + 'hello': pass | ||
@@ -165,4 +174,4 @@ def func(expensive_rename): | ||
| case None if hello + hello + hello + hello: pass | ||
| case None if expensive_rename: pass | ||
| case 'hello' if 'hello' + 'hello' + 'hello' + 'hello': pass | ||
| case None if expensive_rename: pass | ||
| case 'hello' if 'hello' + 'hello' + 'hello' + 'hello': pass | ||
| ''' | ||
@@ -175,3 +184,3 @@ | ||
| case None if B + B + B + B: pass | ||
| case 'hello' if A + A + A + A: pass | ||
| case 'hello' if A + A + A + A: pass | ||
@@ -182,8 +191,8 @@ def C(expensive_rename): | ||
| case None if hello + hello + hello + hello: pass | ||
| case None if expensive_rename: pass | ||
| case 'hello' if A + A + A + A: pass | ||
| case None if expensive_rename: pass | ||
| case 'hello' if A + A + A + A: pass | ||
| ''' | ||
| expected_ast = ast.parse(expected) | ||
| actual_ast = rename(source, locals=False, globals=True) | ||
| actual_ast = do_rename(source, allow_locals=False, allow_globals=True) | ||
| assert_code(expected_ast, actual_ast) | ||
@@ -205,7 +214,7 @@ | ||
| case None: hello + hello + hello + hello | ||
| case None: expensive_rename | ||
| case None: expensive_rename | ||
| ''' | ||
| expected = ''' | ||
| sausage=0 | ||
| sausage=0 | ||
| match None: | ||
@@ -218,7 +227,7 @@ case None: sausage + sausage + sausage + sausage | ||
| case None: A + A + A + A | ||
| case None: expensive_rename | ||
| case None: expensive_rename | ||
| ''' | ||
| expected_ast = ast.parse(expected) | ||
| actual_ast = rename(source, locals=True, globals=False) | ||
| actual_ast = do_rename(source, allow_locals=True, allow_globals=False) | ||
| assert_code(expected_ast, actual_ast) | ||
@@ -232,3 +241,3 @@ | ||
| source = ''' | ||
| sausage=0 | ||
| sausage=0 | ||
| match None: | ||
@@ -241,3 +250,3 @@ case None: sausage + sausage + sausage + sausage | ||
| case None: hello + hello + hello + hello | ||
| case None: expensive_rename | ||
| case None: expensive_rename | ||
| ''' | ||
@@ -254,7 +263,7 @@ | ||
| case None: hello + hello + hello + hello | ||
| case None: expensive_rename | ||
| case None: expensive_rename | ||
| ''' | ||
| expected_ast = ast.parse(expected) | ||
| actual_ast = rename(source, locals=False, globals=True) | ||
| actual_ast = do_rename(source, allow_locals=False, allow_globals=True) | ||
| assert_code(expected_ast, actual_ast) | ||
@@ -276,3 +285,3 @@ | ||
| case [True, True, True, True]: pass | ||
| case [False, False, False, False]: pass | ||
| case [False, False, False, False]: pass | ||
@@ -285,7 +294,7 @@ def func(expensive_rename): | ||
| case Global(d, e) if d: e | ||
| case Local(f, g) if f: g | ||
| case Local(f, g) if f: g | ||
| case ['hello', 'hello', 'hello', 'hello']: pass | ||
| case [None, None, None, None]: pass | ||
| case [True, True, True, True]: pass | ||
| case [False, False, False, False]: pass | ||
| case [False, False, False, False]: pass | ||
| ''' | ||
@@ -302,3 +311,3 @@ | ||
| case [True, True, True, True]: pass | ||
| case [False, False, False, False]: pass | ||
| case [False, False, False, False]: pass | ||
@@ -315,7 +324,7 @@ def func(expensive_rename): | ||
| case [True, True, True, True]: pass | ||
| case [False, False, False, False]: pass | ||
| case [False, False, False, False]: pass | ||
| ''' | ||
| expected_ast = ast.parse(expected) | ||
| actual_ast = rename(source, locals=True, globals=False) | ||
| actual_ast = do_rename(source, allow_locals=True, allow_globals=False) | ||
| assert_code(expected_ast, actual_ast) | ||
@@ -336,3 +345,3 @@ | ||
| case [True, True, True, True]: pass | ||
| case [False, False, False, False]: pass | ||
| case [False, False, False, False]: pass | ||
@@ -345,7 +354,7 @@ def func(expensive_rename): | ||
| case Global(d, e) if d: e | ||
| case Local(f, g) if f: g | ||
| case Local(f, g) if f: g | ||
| case ['hello', 'hello', 'hello', 'hello']: pass | ||
| case [None, None, None, None]: pass | ||
| case [True, True, True, True]: pass | ||
| case [False, False, False, False]: pass | ||
| case [False, False, False, False]: pass | ||
| ''' | ||
@@ -361,3 +370,3 @@ | ||
| case [True, True, True, True]: pass | ||
| case [False, False, False, False]: pass | ||
| case [False, False, False, False]: pass | ||
@@ -374,7 +383,7 @@ def D(expensive_rename): | ||
| case [True, True, True, True]: pass | ||
| case [False, False, False, False]: pass | ||
| case [False, False, False, False]: pass | ||
| ''' | ||
| expected_ast = ast.parse(expected) | ||
| actual_ast = rename(source, locals=False, globals=True) | ||
| actual_ast = do_rename(source, allow_locals=False, allow_globals=True) | ||
| assert_code(expected_ast, actual_ast) |
+15
-10
| import ast | ||
| import sys | ||
| import pytest | ||
| from python_minifier import unparse | ||
| from python_minifier.ast_compare import compare_ast | ||
| def test_pep635_unparse(): | ||
@@ -17,7 +20,7 @@ if sys.version_info < (3, 10): | ||
| pass | ||
| match node: | ||
| case BinOp("+", a, BinOp("*", b, c)): | ||
| pass | ||
| pass | ||
| match json_pet: | ||
@@ -30,3 +33,3 @@ case {"type": "cat", "name": name, "pattern": pattern}: | ||
| raise ValueError("Not a suitable pet") | ||
| def sort(seq): | ||
@@ -59,3 +62,3 @@ match seq: | ||
| return Num(value) | ||
| def simplify(expr): | ||
@@ -99,3 +102,3 @@ match expr: | ||
| return sum(a) / len(a) | ||
| def is_closed(sequence): | ||
@@ -109,3 +112,3 @@ match sequence: | ||
| return False | ||
| def handle_reply(reply): | ||
@@ -137,2 +140,3 @@ match reply: | ||
| def test_pep646_unparse(): | ||
@@ -305,2 +309,3 @@ if sys.version_info < (3, 10): | ||
| def test_match_unparse(): | ||
@@ -316,6 +321,6 @@ if sys.version_info < (3, 10): | ||
| case _:pass | ||
| match a: | ||
| case 0|(0|0): pass | ||
| case (0|0)|0: pass | ||
| case (0|0)|0: pass | ||
| case 0|0|0: pass | ||
@@ -328,3 +333,3 @@ | ||
| case [action, obj]:pass | ||
| case {**rest}: pass | ||
| case {**rest}: pass | ||
| ''' | ||
@@ -331,0 +336,0 @@ |
@@ -5,2 +5,3 @@ import itertools | ||
| def test_name_generator(): | ||
@@ -7,0 +8,0 @@ |
@@ -7,2 +7,3 @@ import sys | ||
| def test_nonlocal_name(): | ||
@@ -20,3 +21,3 @@ if sys.version_info < (3, 0): | ||
| inner() | ||
| rename_me = False | ||
| rename_me = False | ||
| outer() | ||
@@ -52,3 +53,3 @@ return rename_me | ||
| return 1 | ||
| patch() | ||
@@ -71,2 +72,3 @@ return f() | ||
| def test_nonlocal_import(): | ||
@@ -93,3 +95,3 @@ if sys.version_info < (3, 0): | ||
| exec(test_code, {}, unminified_locals) | ||
| assert unminified_locals['result'] == True | ||
| assert unminified_locals['result'] is True | ||
@@ -100,4 +102,5 @@ minified = python_minifier.minify(test_code, rename_locals=True) | ||
| exec(minified, {}, minified_locals) | ||
| assert minified_locals['result'] == True | ||
| assert minified_locals['result'] is True | ||
| def test_nonlocal_import_alias(): | ||
@@ -124,3 +127,3 @@ if sys.version_info < (3, 0): | ||
| exec(test_code, {}, unminified_locals) | ||
| assert unminified_locals['result'] == True | ||
| assert unminified_locals['result'] is True | ||
@@ -131,2 +134,2 @@ minified = python_minifier.minify(test_code, rename_locals=True) | ||
| exec(minified, {}, minified_locals) | ||
| assert minified_locals['result'] == True | ||
| assert minified_locals['result'] is True |
| import ast | ||
| import sys | ||
| import pytest | ||
| from python_minifier import unparse | ||
@@ -8,2 +10,3 @@ from python_minifier.ast_compare import compare_ast | ||
| def test_pep(): | ||
@@ -37,2 +40,3 @@ if sys.version_info < (3, 8): | ||
| def test_convert(): | ||
@@ -84,2 +88,2 @@ if sys.version_info < (3, 8): | ||
| actual_ast = unparse(remove_posargs(ast.parse(source))) | ||
| compare_ast(expected_ast, ast.parse(actual_ast)) | ||
| compare_ast(expected_ast, ast.parse(actual_ast)) |
| import ast | ||
| import sys | ||
| import pytest | ||
| from python_minifier import add_namespace, RemoveAnnotationsOptions | ||
| from python_minifier import RemoveAnnotationsOptions | ||
| from python_minifier.ast_annotation import add_parent | ||
| from python_minifier.ast_compare import compare_ast | ||
| from python_minifier.rename import add_namespace | ||
| from python_minifier.transforms.remove_annotations import RemoveAnnotations | ||
| from python_minifier.ast_compare import compare_ast | ||
| def remove_annotations(source, **kwargs): | ||
| module = ast.parse(source) | ||
| add_parent(module) | ||
| add_namespace(module) | ||
@@ -15,2 +20,3 @@ RemoveAnnotations(RemoveAnnotationsOptions(**kwargs))(module) | ||
| def test_AnnAssign(): | ||
@@ -36,9 +42,12 @@ | ||
| expected_ast = ast.parse(expected) | ||
| actual_ast = remove_annotations(source, | ||
| remove_variable_annotations=True, | ||
| remove_return_annotations=False, | ||
| remove_argument_annotations=False, | ||
| remove_class_attribute_annotations=False) | ||
| actual_ast = remove_annotations( | ||
| source, | ||
| remove_variable_annotations=True, | ||
| remove_return_annotations=False, | ||
| remove_argument_annotations=False, | ||
| remove_class_attribute_annotations=False | ||
| ) | ||
| compare_ast(expected_ast, actual_ast) | ||
| def test_FunctionDef(): | ||
@@ -55,7 +64,9 @@ if sys.version_info < (3,): | ||
| expected_ast = ast.parse(expected) | ||
| actual_ast = remove_annotations(source, | ||
| remove_variable_annotations=False, | ||
| remove_return_annotations=True, | ||
| remove_argument_annotations=True, | ||
| remove_class_attribute_annotations=False) | ||
| actual_ast = remove_annotations( | ||
| source, | ||
| remove_variable_annotations=False, | ||
| remove_return_annotations=True, | ||
| remove_argument_annotations=True, | ||
| remove_class_attribute_annotations=False | ||
| ) | ||
| compare_ast(expected_ast, actual_ast) | ||
@@ -70,7 +81,9 @@ | ||
| expected_ast = ast.parse(expected) | ||
| actual_ast = remove_annotations(source, | ||
| remove_variable_annotations=False, | ||
| remove_return_annotations=False, | ||
| remove_argument_annotations=True, | ||
| remove_class_attribute_annotations=False) | ||
| actual_ast = remove_annotations( | ||
| source, | ||
| remove_variable_annotations=False, | ||
| remove_return_annotations=False, | ||
| remove_argument_annotations=True, | ||
| remove_class_attribute_annotations=False | ||
| ) | ||
| compare_ast(expected_ast, actual_ast) | ||
@@ -91,5 +104,6 @@ | ||
| remove_class_attribute_annotations=False | ||
| ) | ||
| ) | ||
| compare_ast(expected_ast, actual_ast) | ||
| def test_AsyncFunctionDef(): | ||
@@ -105,9 +119,12 @@ if sys.version_info < (3, 6): | ||
| expected_ast = ast.parse(expected) | ||
| actual_ast = remove_annotations(source, | ||
| remove_variable_annotations=True, | ||
| remove_return_annotations=True, | ||
| remove_argument_annotations=True, | ||
| remove_class_attribute_annotations=False) | ||
| actual_ast = remove_annotations( | ||
| source, | ||
| remove_variable_annotations=True, | ||
| remove_return_annotations=True, | ||
| remove_argument_annotations=True, | ||
| remove_class_attribute_annotations=False | ||
| ) | ||
| compare_ast(expected_ast, actual_ast) | ||
| def test_AnnAssign_novalue(): | ||
@@ -129,9 +146,12 @@ if sys.version_info < (3, 6): | ||
| expected_ast = ast.parse(expected) | ||
| actual_ast = remove_annotations(source, | ||
| remove_variable_annotations=True, | ||
| remove_return_annotations=False, | ||
| remove_argument_annotations=False, | ||
| remove_class_attribute_annotations=False) | ||
| actual_ast = remove_annotations( | ||
| source, | ||
| remove_variable_annotations=True, | ||
| remove_return_annotations=False, | ||
| remove_argument_annotations=False, | ||
| remove_class_attribute_annotations=False | ||
| ) | ||
| compare_ast(expected_ast, actual_ast) | ||
| def test_class_attributes(): | ||
@@ -155,7 +175,9 @@ if sys.version_info < (3, 6): | ||
| expected_ast = ast.parse(expected) | ||
| actual_ast = remove_annotations(source, | ||
| remove_variable_annotations=False, | ||
| remove_return_annotations=False, | ||
| remove_argument_annotations=False, | ||
| remove_class_attribute_annotations=True) | ||
| actual_ast = remove_annotations( | ||
| source, | ||
| remove_variable_annotations=False, | ||
| remove_return_annotations=False, | ||
| remove_argument_annotations=False, | ||
| remove_class_attribute_annotations=True | ||
| ) | ||
| compare_ast(expected_ast, actual_ast) | ||
@@ -195,9 +217,12 @@ | ||
| expected_ast = ast.parse(expected) | ||
| actual_ast = remove_annotations(source, | ||
| remove_variable_annotations=False, | ||
| remove_return_annotations=False, | ||
| remove_argument_annotations=False, | ||
| remove_class_attribute_annotations=True) | ||
| actual_ast = remove_annotations( | ||
| source, | ||
| remove_variable_annotations=False, | ||
| remove_return_annotations=False, | ||
| remove_argument_annotations=False, | ||
| remove_class_attribute_annotations=True | ||
| ) | ||
| compare_ast(expected_ast, actual_ast) | ||
| def test_remove_dataclass(): | ||
@@ -223,7 +248,9 @@ if sys.version_info < (3, 6): | ||
| expected_ast = ast.parse(expected) | ||
| actual_ast = remove_annotations(source, | ||
| remove_variable_annotations=False, | ||
| remove_return_annotations=False, | ||
| remove_argument_annotations=False, | ||
| remove_class_attribute_annotations=True) | ||
| actual_ast = remove_annotations( | ||
| source, | ||
| remove_variable_annotations=False, | ||
| remove_return_annotations=False, | ||
| remove_argument_annotations=False, | ||
| remove_class_attribute_annotations=True | ||
| ) | ||
| compare_ast(expected_ast, actual_ast) | ||
@@ -252,9 +279,12 @@ | ||
| expected_ast = ast.parse(expected) | ||
| actual_ast = remove_annotations(source, | ||
| remove_variable_annotations=False, | ||
| remove_return_annotations=False, | ||
| remove_argument_annotations=False, | ||
| remove_class_attribute_annotations=True) | ||
| actual_ast = remove_annotations( | ||
| source, | ||
| remove_variable_annotations=False, | ||
| remove_return_annotations=False, | ||
| remove_argument_annotations=False, | ||
| remove_class_attribute_annotations=True | ||
| ) | ||
| compare_ast(expected_ast, actual_ast) | ||
| def test_remove(): | ||
@@ -283,7 +313,9 @@ if sys.version_info < (3, 6): | ||
| expected_ast = ast.parse(expected) | ||
| actual_ast = remove_annotations(source, | ||
| remove_variable_annotations=False, | ||
| remove_return_annotations=False, | ||
| remove_argument_annotations=False, | ||
| remove_class_attribute_annotations=True) | ||
| actual_ast = remove_annotations( | ||
| source, | ||
| remove_variable_annotations=False, | ||
| remove_return_annotations=False, | ||
| remove_argument_annotations=False, | ||
| remove_class_attribute_annotations=True | ||
| ) | ||
| compare_ast(expected_ast, actual_ast) | ||
@@ -317,6 +349,6 @@ | ||
| mysecondfile: str | ||
| class Dummy(HypedDict): | ||
| myfield: 0 | ||
| mysecondfile: 0 | ||
| mysecondfile: 0 | ||
@@ -326,14 +358,16 @@ class Dummy(typing.TypedDict): | ||
| mysecondfile: str | ||
| class Dummy(typing.TypedDic): | ||
| myfield: 0 | ||
| mysecondfile: 0 | ||
| ''' | ||
| expected_ast = ast.parse(expected) | ||
| actual_ast = remove_annotations(source, | ||
| remove_variable_annotations=False, | ||
| remove_return_annotations=False, | ||
| remove_argument_annotations=False, | ||
| remove_class_attribute_annotations=True) | ||
| actual_ast = remove_annotations( | ||
| source, | ||
| remove_variable_annotations=False, | ||
| remove_return_annotations=False, | ||
| remove_argument_annotations=False, | ||
| remove_class_attribute_annotations=True | ||
| ) | ||
| compare_ast(expected_ast, actual_ast) |
| import ast | ||
| from python_minifier import add_namespace, bind_names, resolve_names | ||
| from python_minifier.ast_annotation import add_parent | ||
| from python_minifier.ast_compare import compare_ast | ||
| from python_minifier.rename import add_namespace, bind_names, resolve_names | ||
| from python_minifier.transforms.remove_asserts import RemoveAsserts | ||
| from python_minifier.ast_compare import compare_ast | ||
| def remove_asserts(source): | ||
| module = ast.parse(source, 'remove_asserts') | ||
| add_parent(module) | ||
| add_namespace(module) | ||
@@ -14,2 +18,3 @@ bind_names(module) | ||
| def test_remove_assert_empty_module(): | ||
@@ -23,2 +28,3 @@ source = 'assert False' | ||
| def test_remove_assert_module(): | ||
@@ -36,2 +42,3 @@ source = '''import collections | ||
| def test_remove_if_empty(): | ||
@@ -47,2 +54,3 @@ source = '''if True: | ||
| def test_remove_if_line(): | ||
@@ -56,10 +64,11 @@ source = '''if True: assert False''' | ||
| def test_remove_suite(): | ||
| source = '''if True: | ||
| source = '''if True: | ||
| assert False | ||
| a=1 | ||
| assert False | ||
| a=1 | ||
| assert False | ||
| return None''' | ||
| expected = '''if True: | ||
| a=1 | ||
| a=1 | ||
| return None''' | ||
@@ -71,2 +80,3 @@ | ||
| def test_remove_from_class(): | ||
@@ -92,2 +102,3 @@ source = '''class A: | ||
| def test_remove_from_class_empty(): | ||
@@ -103,2 +114,3 @@ source = '''class A: | ||
| def test_remove_from_class_func_empty(): | ||
@@ -105,0 +117,0 @@ source = '''class A: |
@@ -5,4 +5,5 @@ import ast | ||
| from python_minifier import add_namespace, bind_names, resolve_names | ||
| from python_minifier.ast_annotation import add_parent | ||
| from python_minifier.ast_compare import compare_ast | ||
| from python_minifier.rename import add_namespace, bind_names, resolve_names | ||
| from python_minifier.transforms.remove_debug import RemoveDebug | ||
@@ -14,2 +15,3 @@ | ||
| add_parent(module) | ||
| add_namespace(module) | ||
@@ -54,9 +56,9 @@ bind_names(module) | ||
| def test_remove_suite(): | ||
| source = '''if True: | ||
| source = '''if True: | ||
| if __debug__: pass | ||
| a=1 | ||
| if __debug__: pass | ||
| a=1 | ||
| if __debug__: pass | ||
| return None''' | ||
| expected = '''if True: | ||
| a=1 | ||
| a=1 | ||
| return None''' | ||
@@ -114,8 +116,10 @@ | ||
| @pytest.mark.parametrize("condition", [ | ||
| '__debug__', | ||
| '__debug__ is True', | ||
| '__debug__ is not False', | ||
| '__debug__ == True' | ||
| ]) | ||
| @pytest.mark.parametrize( | ||
| 'condition', [ | ||
| '__debug__', | ||
| '__debug__ is True', | ||
| '__debug__ is not False', | ||
| '__debug__ == True' | ||
| ] | ||
| ) | ||
| def test_remove_truthy_debug(condition): | ||
@@ -129,3 +133,3 @@ source = ''' | ||
| print(value) | ||
| print(value) | ||
| ''' | ||
@@ -136,3 +140,3 @@ | ||
| print(value) | ||
| print(value) | ||
| ''' | ||
@@ -145,11 +149,13 @@ | ||
| @pytest.mark.parametrize("condition", [ | ||
| 'not __debug__', | ||
| '__debug__ is False', | ||
| '__debug__ is not True', | ||
| '__debug__ == False', | ||
| 'not __debug__ is True', | ||
| 'not __debug__ is not False', | ||
| 'not __debug__ == True' | ||
| ]) | ||
| @pytest.mark.parametrize( | ||
| 'condition', [ | ||
| 'not __debug__', | ||
| '__debug__ is False', | ||
| '__debug__ is not True', | ||
| '__debug__ == False', | ||
| 'not __debug__ is True', | ||
| 'not __debug__ is not False', | ||
| 'not __debug__ == True' | ||
| ] | ||
| ) | ||
| def test_no_remove_falsy_debug(condition): | ||
@@ -163,3 +169,3 @@ source = ''' | ||
| print(value) | ||
| print(value) | ||
| ''' | ||
@@ -166,0 +172,0 @@ |
@@ -6,4 +6,5 @@ import ast | ||
| from python_minifier.ast_annotation import add_parent | ||
| from python_minifier.ast_compare import compare_ast | ||
| from python_minifier.rename import add_namespace, bind_names, resolve_names | ||
| from python_minifier.ast_compare import compare_ast | ||
| from python_minifier.transforms.remove_exception_brackets import remove_no_arg_exception_call | ||
@@ -15,2 +16,3 @@ | ||
| add_parent(module) | ||
| add_namespace(module) | ||
@@ -34,2 +36,3 @@ bind_names(module) | ||
| def test_zero_division_error_brackets(): | ||
@@ -47,2 +50,3 @@ """This is a buitin so remove the brackets""" | ||
| def test_builtin_with_arg(): | ||
@@ -60,2 +64,3 @@ """This has an arg so dont' remove the brackets""" | ||
| def test_one_division_error_brackets(): | ||
@@ -73,2 +78,3 @@ """This is not a builtin so don't remove the brackets even though it's not defined in the module""" | ||
| def test_redefined(): | ||
@@ -97,2 +103,3 @@ """This is usually a builtin, but don't remove brackets if it's been redefined""" | ||
| def test_raise_from(): | ||
@@ -110,2 +117,3 @@ """This is a builtin so remove the brackets""" | ||
| def test_raise_from_only(): | ||
@@ -123,2 +131,3 @@ """This is a builtin so remove the brackets""" | ||
| def test_raise_from_arg(): | ||
@@ -136,2 +145,3 @@ """This is a builtin so remove the brackets""" | ||
| def test_raise_builtin_in_class(): | ||
@@ -155,2 +165,3 @@ """This is a builtin so remove the brackets""" | ||
| def test_raise_redefined_builtin_in_class(): | ||
@@ -176,2 +187,2 @@ """This was a builtin at some point, but it was redefined so don't remove the brackets""" | ||
| actual_ast = remove_brackets(source) | ||
| compare_ast(expected_ast, actual_ast) | ||
| compare_ast(expected_ast, actual_ast) |
| import ast | ||
| from python_minifier import add_namespace, bind_names, resolve_names | ||
| from python_minifier.ast_annotation import add_parent | ||
| from python_minifier.ast_compare import compare_ast | ||
| from python_minifier.rename import add_namespace, bind_names, resolve_names | ||
| from python_minifier.transforms.remove_literal_statements import RemoveLiteralStatements | ||
| from python_minifier.ast_compare import compare_ast | ||
| def remove_literals(source): | ||
| module = ast.parse(source, 'test_remove_literal_statements') | ||
| add_parent(module) | ||
| add_namespace(module) | ||
@@ -15,2 +18,3 @@ bind_names(module) | ||
| def test_remove_literal_num(): | ||
@@ -24,2 +28,3 @@ source = '213' | ||
| def test_remove_literal_str(): | ||
@@ -33,2 +38,3 @@ source = '"hello"' | ||
| def test_complex(): | ||
@@ -35,0 +41,0 @@ source = ''' |
| import ast | ||
| import pytest | ||
| import sys | ||
| import pytest | ||
| from python_minifier.ast_compare import compare_ast | ||
| from python_minifier.transforms.remove_object_base import RemoveObject | ||
| from python_minifier.ast_compare import compare_ast | ||
@@ -70,2 +71,1 @@ | ||
| compare_ast(expected_ast, actual_ast) | ||
| import ast | ||
| from python_minifier import add_namespace, bind_names, resolve_names | ||
| from python_minifier.ast_annotation import add_parent | ||
| from python_minifier.ast_compare import compare_ast | ||
| from python_minifier.rename import add_namespace, bind_names, resolve_names | ||
| from python_minifier.transforms.remove_pass import RemovePass | ||
| from python_minifier.ast_compare import compare_ast | ||
| def remove_literals(source): | ||
| module = ast.parse(source, 'remove_literals') | ||
| add_parent(module) | ||
| add_namespace(module) | ||
@@ -14,2 +18,3 @@ bind_names(module) | ||
| def test_remove_pass_empty_module(): | ||
@@ -23,2 +28,3 @@ source = 'pass' | ||
| def test_remove_pass_module(): | ||
@@ -36,2 +42,3 @@ source = '''import collections | ||
| def test_remove_if_empty(): | ||
@@ -47,2 +54,3 @@ source = '''if True: | ||
| def test_remove_if_line(): | ||
@@ -56,10 +64,11 @@ source = '''if True: pass''' | ||
| def test_remove_suite(): | ||
| source = '''if True: | ||
| source = '''if True: | ||
| pass | ||
| a=1 | ||
| pass | ||
| a=1 | ||
| pass | ||
| return None''' | ||
| expected = '''if True: | ||
| a=1 | ||
| a=1 | ||
| return None''' | ||
@@ -71,2 +80,3 @@ | ||
| def test_remove_from_class(): | ||
@@ -92,2 +102,3 @@ source = '''class A: | ||
| def test_remove_from_class_empty(): | ||
@@ -103,2 +114,3 @@ source = '''class A: | ||
| def test_remove_from_class_func_empty(): | ||
@@ -105,0 +117,0 @@ source = '''class A: |
@@ -8,13 +8,13 @@ """ | ||
| import ast | ||
| import sys | ||
| import pytest | ||
| from python_minifier import unparse | ||
| from python_minifier.ast_annotation import add_parent | ||
| from python_minifier.ast_compare import CompareError, compare_ast | ||
| from python_minifier.rename import add_namespace, allow_rename_globals, allow_rename_locals, bind_names, rename, resolve_names | ||
| from python_minifier import add_namespace, bind_names, resolve_names, allow_rename_locals, allow_rename_globals, \ | ||
| compare_ast, rename, CompareError, unparse | ||
| def do_rename(source): | ||
| # This will raise if the source file can't be parsed | ||
| module = ast.parse(source, 'test_rename_bultins') | ||
| add_parent(module) | ||
| add_namespace(module) | ||
@@ -24,4 +24,4 @@ bind_names(module) | ||
| allow_rename_locals(module, True) | ||
| allow_rename_globals(module, True) | ||
| allow_rename_locals(module, rename_locals=True) | ||
| allow_rename_globals(module, rename_globals=True) | ||
@@ -80,2 +80,3 @@ rename(module) | ||
| def test_rename_local_builtin(): | ||
@@ -104,2 +105,3 @@ source = ''' | ||
| def test_no_rename_local_assigned_builtin(): | ||
@@ -106,0 +108,0 @@ source = ''' |
@@ -12,4 +12,6 @@ """ | ||
| from python_minifier import add_namespace, bind_names, resolve_names, allow_rename_locals, allow_rename_globals, \ | ||
| compare_ast, rename, CompareError, unparse | ||
| from python_minifier import unparse | ||
| from python_minifier.ast_annotation import add_parent | ||
| from python_minifier.ast_compare import CompareError, compare_ast | ||
| from python_minifier.rename import add_namespace, allow_rename_globals, allow_rename_locals, bind_names, rename, resolve_names | ||
@@ -21,2 +23,3 @@ | ||
| module = ast.parse(source, 'test_rename_locals') | ||
| add_parent(module) | ||
| add_namespace(module) | ||
@@ -26,4 +29,4 @@ bind_names(module) | ||
| allow_rename_locals(module, True) | ||
| allow_rename_globals(module, False) | ||
| allow_rename_locals(module, rename_locals=True) | ||
| allow_rename_globals(module, rename_globals=False) | ||
@@ -34,2 +37,3 @@ rename(module) | ||
| def assert_code(expected_ast, actual_ast): | ||
@@ -58,2 +62,3 @@ try: | ||
| def test_rename_multiple_definitions(): | ||
@@ -78,2 +83,3 @@ source = ''' | ||
| def test_rename_self_cls_in_place(): | ||
@@ -89,23 +95,23 @@ source = ''' | ||
| def mystatic(cls): return cls | ||
| @staticmethod | ||
| @staticmethod | ||
| def mystatic(asdfghjkl): return asdfghjkl | ||
| @staticmethod | ||
| @staticmethod | ||
| def mystatic(): return 'No cls' | ||
| @classmethod | ||
| def mystatic(cls): return cls | ||
| @classmethod | ||
| @classmethod | ||
| def mystatic(asdfghjkl): return asdfghjkl | ||
| @classmethod | ||
| @classmethod | ||
| def mystatic(): return 'No cls' | ||
| @unknown_decorator | ||
| def unknown(self): return self | ||
| @unknown_decorator | ||
| @unknown_decorator | ||
| def unknown(qwertyuiop): return qwertyuiop | ||
| @unknown_decorator | ||
| @unknown_decorator | ||
| def unknown(self, arg): return self, arg | ||
| @unknown_decorator | ||
| def unknown(): return 'No arg' | ||
| @unknown_decorator | ||
| def unknown(): return 'No arg' | ||
| ''' | ||
@@ -121,22 +127,22 @@ expected = ''' | ||
| def mystatic(cls): return cls | ||
| @staticmethod | ||
| @staticmethod | ||
| def mystatic(asdfghjkl): return asdfghjkl | ||
| @staticmethod | ||
| @staticmethod | ||
| def mystatic(): return 'No cls' | ||
| @classmethod | ||
| def mystatic(A): return A | ||
| @classmethod | ||
| @classmethod | ||
| def mystatic(A): return A | ||
| @classmethod | ||
| @classmethod | ||
| def mystatic(): return 'No cls' | ||
| @unknown_decorator | ||
| def unknown(self): return self | ||
| @unknown_decorator | ||
| @unknown_decorator | ||
| def unknown(qwertyuiop): return qwertyuiop | ||
| @unknown_decorator | ||
| @unknown_decorator | ||
| def unknown(self, arg): return self, arg | ||
| @unknown_decorator | ||
| def unknown(): return 'No arg' | ||
| @unknown_decorator | ||
| def unknown(): return 'No arg' | ||
| ''' | ||
@@ -153,6 +159,6 @@ | ||
| def samename(arg, arg2, *asd, **asd): return asd | ||
| class TestClass(): | ||
| def mymethod(self, arg, *args, **kwargs): return args, kwargs | ||
| @classmethod | ||
@@ -164,8 +170,8 @@ def mymethod(cls, arg, *args, **kwargs): return args, kwargs | ||
| def samename(arg, arg2, *A, **A): return A | ||
| class TestClass(): | ||
| def mymethod(C, arg, *A, **B): return A, B | ||
| @classmethod | ||
| def mymethod(C, arg, *A, **B): return A, B | ||
| def mymethod(C, arg, *A, **B): return A, B | ||
| ''' | ||
@@ -271,3 +277,3 @@ | ||
| print(this_is_my_long_argument_name) | ||
| print(this_is_my_long_argument_name) | ||
| print(this_is_my_long_argument_name) | ||
| ''' | ||
@@ -285,2 +291,3 @@ expected = ''' | ||
| def test_no_rename_single_char_arg(): | ||
@@ -292,14 +299,14 @@ source = ''' | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| print(a) | ||
| ''' | ||
@@ -311,2 +318,3 @@ | ||
| def test_rename_arg(): | ||
@@ -319,3 +327,3 @@ source = ''' | ||
| print(aa) | ||
| print(aa) | ||
| print(aa) | ||
| ''' | ||
@@ -329,3 +337,3 @@ expected = ''' | ||
| print(A) | ||
| print(A) | ||
| print(A) | ||
| ''' | ||
@@ -337,5 +345,6 @@ | ||
| def test_no_rename_lambda_arg(): | ||
| source = ''' | ||
| lambda my_argument: f(my_argument + my_argument + my_argument) | ||
| lambda my_argument: f(my_argument + my_argument + my_argument) | ||
| ''' | ||
@@ -347,9 +356,10 @@ | ||
| def test_rename_lambda_stararg(): | ||
| source = ''' | ||
| lambda *args, **kwargs: f(args, kwargs) | ||
| lambda *args, **kwargs: f(args, kwargs) | ||
| ''' | ||
| expected = ''' | ||
| lambda *A, **B: f(A, B) | ||
| lambda *A, **B: f(A, B) | ||
| ''' | ||
@@ -361,2 +371,3 @@ | ||
| def test_python3_listcomp_scope(): | ||
@@ -367,7 +378,7 @@ if sys.version_info < (3, 0): | ||
| source = ''' | ||
| [a for a in mylist] | ||
| [a for a in mylist] | ||
| ''' | ||
| expected = ''' | ||
| [A for A in mylist] | ||
| [A for A in mylist] | ||
| ''' | ||
@@ -379,2 +390,3 @@ | ||
| def test_python2_listcomp_scope(): | ||
@@ -385,13 +397,13 @@ if sys.version_info >= (3, 0): | ||
| source = ''' | ||
| [a for a in mylist] | ||
| [a for a in mylist] | ||
| def t(): | ||
| a = True | ||
| [a for a in mylist] | ||
| [a for a in mylist] | ||
| ''' | ||
| expected = ''' | ||
| [a for a in mylist] | ||
| [a for a in mylist] | ||
| def t(): | ||
| A = True | ||
| [A for A in mylist] | ||
| [A for A in mylist] | ||
| ''' | ||
@@ -403,2 +415,3 @@ | ||
| def test_arg_rename(): | ||
@@ -416,6 +429,7 @@ | ||
| def test_multiple_args(): | ||
| source = ''' | ||
| def f(hello, hello): | ||
| print(hello + hello) | ||
| print(hello + hello) | ||
| ''' | ||
@@ -422,0 +436,0 @@ expected = ''' |
@@ -6,2 +6,3 @@ import ast | ||
| def test_slice(): | ||
@@ -8,0 +9,0 @@ """AST for slices was changed in 3.9""" |
| import ast | ||
| import sys | ||
| import pytest | ||
| from python_minifier import unparse | ||
| from python_minifier.ast_compare import compare_ast | ||
| # There are bizarrely few examples of this, some in the PEP are even syntax errors | ||
@@ -26,2 +29,3 @@ | ||
| def test_pep696_2(): | ||
@@ -50,2 +54,3 @@ if sys.version_info < (3, 13): | ||
| def test_pep696_3(): | ||
@@ -68,2 +73,3 @@ if sys.version_info < (3, 13): | ||
| def test_example(): | ||
@@ -92,2 +98,2 @@ if sys.version_info < (3, 13): | ||
| actual_ast = unparse(expected_ast) | ||
| compare_ast(expected_ast, ast.parse(actual_ast)) | ||
| compare_ast(expected_ast, ast.parse(actual_ast)) |
| import sys | ||
| import pytest | ||
| import python_minifier.ast_compat as ast | ||
| from python_minifier.util import is_ast_node | ||
| def test_type_nodes(): | ||
| assert is_ast_node(ast.Str('a'), ast.Str) | ||
| if hasattr(ast, 'Bytes'): | ||
| assert is_ast_node(ast.Bytes(b'a'), ast.Bytes) | ||
| assert is_ast_node(ast.Num(1), ast.Num) | ||
| assert is_ast_node(ast.Num(0), ast.Num) | ||
| if hasattr(ast, 'NameConstant'): | ||
| assert is_ast_node(ast.NameConstant(True), ast.NameConstant) | ||
| assert is_ast_node(ast.NameConstant(False), ast.NameConstant) | ||
| assert is_ast_node(ast.NameConstant(None), ast.NameConstant) | ||
| else: | ||
| assert is_ast_node(ast.Name(id='True', ctx=ast.Load()), ast.Name) | ||
| assert is_ast_node(ast.Name(id='False', ctx=ast.Load()), ast.Name) | ||
| assert is_ast_node(ast.Name(id='None', ctx=ast.Load()), ast.Name) | ||
| assert is_ast_node(ast.Ellipsis(), ast.Ellipsis) | ||
| def test_constant_nodes(): | ||
| # only test on python 3.8+ | ||
| if sys.version_info < (3, 8): | ||
| pytest.skip('Constant not available') | ||
| assert is_ast_node(ast.Constant('a'), ast.Str) | ||
| assert is_ast_node(ast.Constant(b'a'), ast.Bytes) | ||
| assert is_ast_node(ast.Constant(1), ast.Num) | ||
| assert is_ast_node(ast.Constant(0), ast.Num) | ||
| assert is_ast_node(ast.Constant(True), ast.NameConstant) | ||
| assert is_ast_node(ast.Constant(False), ast.NameConstant) | ||
| assert is_ast_node(ast.Constant(None), ast.NameConstant) | ||
| assert is_ast_node(ast.Constant(ast.literal_eval('...')), ast.Ellipsis) |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
412085
2.53%86
2.38%10800
3.66%