python-minifier 2.11.2__tar.gz → 2.11.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_minifier-2.11.2/src/python_minifier.egg-info → python_minifier-2.11.3}/PKG-INFO +1 -1
- {python_minifier-2.11.2 → python_minifier-2.11.3}/setup.py +1 -1
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/__init__.py +12 -8
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/__init__.pyi +6 -1
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/__main__.py +10 -7
- python_minifier-2.11.3/src/python_minifier/ast_annotation/__init__.py +82 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/ast_compare.py +1 -3
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/ast_compat.py +38 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/ast_printer.py +8 -5
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/expression_printer.py +14 -13
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/f_string.py +13 -14
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/ministring.py +3 -3
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/module_printer.py +13 -14
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/rename/__init__.py +1 -1
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/rename/bind_names.py +2 -1
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/rename/binding.py +39 -40
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/rename/mapper.py +66 -50
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/rename/rename_literals.py +17 -15
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/rename/renamer.py +26 -28
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/rename/resolve_names.py +8 -7
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/rename/util.py +7 -5
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/token_printer.py +3 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/transforms/combine_imports.py +0 -1
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/transforms/constant_folding.py +16 -10
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/transforms/remove_annotations.py +18 -16
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/transforms/remove_annotations_options.py +3 -1
- python_minifier-2.11.3/src/python_minifier/transforms/remove_annotations_options.pyi +18 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/transforms/remove_asserts.py +1 -2
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/transforms/remove_debug.py +4 -3
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/transforms/remove_exception_brackets.py +12 -7
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/transforms/remove_explicit_return_none.py +5 -4
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/transforms/remove_literal_statements.py +5 -6
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/transforms/remove_object_base.py +2 -1
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/transforms/remove_pass.py +1 -2
- python_minifier-2.11.3/src/python_minifier/transforms/remove_posargs.py +12 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/transforms/suite_transformer.py +6 -5
- python_minifier-2.11.3/src/python_minifier/util.py +42 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3/src/python_minifier.egg-info}/PKG-INFO +1 -1
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier.egg-info/SOURCES.txt +3 -1
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_assignment_expressions.py +5 -2
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_await_fstring.py +3 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_bind_names.py +61 -7
- python_minifier-2.11.3/test/test_bind_names_namedexpr.py +259 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_bind_names_python2.py +1 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_bind_names_python312.py +10 -1
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_bind_names_python313.py +10 -1
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_bind_names_python33.py +1 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_combine_imports.py +17 -8
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_comprehension_rename.py +6 -1
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_decorator_expressions.py +5 -2
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_dict_expansion.py +3 -0
- python_minifier-2.11.3/test/test_folding.py +151 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_fstring.py +19 -16
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_generic_types.py +5 -2
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_hoist_literals.py +41 -11
- python_minifier-2.11.3/test/test_import.py +32 -0
- python_minifier-2.11.3/test/test_is_constant_node.py +43 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_iterable_unpacking.py +3 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_match.py +15 -10
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_match_rename.py +55 -46
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_name_generator.py +1 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_nonlocal.py +9 -6
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_posargs.py +5 -1
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_remove_annotations.py +96 -62
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_remove_assert.py +18 -6
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_remove_debug.py +29 -23
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_remove_exception_brackets.py +13 -2
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_remove_literal_statements.py +8 -2
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_remove_pass.py +18 -6
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_rename_builtins.py +9 -7
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_rename_locals.py +69 -55
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_slice.py +1 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_type_param_defaults.py +7 -1
- python_minifier-2.11.2/src/python_minifier/transforms/remove_annotations_options.pyi +0 -17
- python_minifier-2.11.2/src/python_minifier/transforms/remove_posargs.py +0 -13
- python_minifier-2.11.2/src/python_minifier/util.py +0 -48
- python_minifier-2.11.2/test/test_folding.py +0 -136
- python_minifier-2.11.2/test/test_import.py +0 -30
- python_minifier-2.11.2/test/test_is_ast_node.py +0 -40
- {python_minifier-2.11.2 → python_minifier-2.11.3}/LICENSE +0 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/README.md +0 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/setup.cfg +0 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/py.typed +0 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/rename/name_generator.py +0 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier/transforms/__init__.py +0 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier.egg-info/dependency_links.txt +0 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier.egg-info/entry_points.txt +0 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier.egg-info/top_level.txt +0 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/src/python_minifier.egg-info/zip-safe +0 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_preserve_shebang.py +0 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_remove_explicit_return_none.py +0 -0
- {python_minifier-2.11.2 → python_minifier-2.11.3}/test/test_remove_object.py +3 -3
|
@@ -4,29 +4,30 @@ a 'minified' representation of the same source code.
|
|
|
4
4
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
import python_minifier.ast_compat as ast
|
|
8
7
|
import re
|
|
9
8
|
|
|
9
|
+
import python_minifier.ast_compat as ast
|
|
10
|
+
from python_minifier.ast_annotation import add_parent
|
|
11
|
+
|
|
10
12
|
from python_minifier.ast_compare import CompareError, compare_ast
|
|
11
13
|
from python_minifier.module_printer import ModulePrinter
|
|
12
14
|
from python_minifier.rename import (
|
|
13
|
-
|
|
14
|
-
bind_names,
|
|
15
|
-
resolve_names,
|
|
16
|
-
rename,
|
|
15
|
+
add_namespace,
|
|
17
16
|
allow_rename_globals,
|
|
18
17
|
allow_rename_locals,
|
|
19
|
-
|
|
18
|
+
bind_names,
|
|
19
|
+
rename,
|
|
20
|
+
rename_literals,
|
|
21
|
+
resolve_names
|
|
20
22
|
)
|
|
21
|
-
|
|
22
23
|
from python_minifier.transforms.combine_imports import CombineImports
|
|
23
24
|
from python_minifier.transforms.constant_folding import FoldConstants
|
|
24
25
|
from python_minifier.transforms.remove_annotations import RemoveAnnotations
|
|
25
26
|
from python_minifier.transforms.remove_annotations_options import RemoveAnnotationsOptions
|
|
26
27
|
from python_minifier.transforms.remove_asserts import RemoveAsserts
|
|
27
28
|
from python_minifier.transforms.remove_debug import RemoveDebug
|
|
28
|
-
from python_minifier.transforms.remove_explicit_return_none import RemoveExplicitReturnNone
|
|
29
29
|
from python_minifier.transforms.remove_exception_brackets import remove_no_arg_exception_call
|
|
30
|
+
from python_minifier.transforms.remove_explicit_return_none import RemoveExplicitReturnNone
|
|
30
31
|
from python_minifier.transforms.remove_literal_statements import RemoveLiteralStatements
|
|
31
32
|
from python_minifier.transforms.remove_object_base import RemoveObject
|
|
32
33
|
from python_minifier.transforms.remove_pass import RemovePass
|
|
@@ -115,6 +116,7 @@ def minify(
|
|
|
115
116
|
# This will raise if the source file can't be parsed
|
|
116
117
|
module = ast.parse(source, filename)
|
|
117
118
|
|
|
119
|
+
add_parent(module)
|
|
118
120
|
add_namespace(module)
|
|
119
121
|
|
|
120
122
|
if remove_literal_statements:
|
|
@@ -198,6 +200,7 @@ def minify(
|
|
|
198
200
|
|
|
199
201
|
return minified
|
|
200
202
|
|
|
203
|
+
|
|
201
204
|
def _find_shebang(source):
|
|
202
205
|
"""
|
|
203
206
|
Find a shebang line in source
|
|
@@ -214,6 +217,7 @@ def _find_shebang(source):
|
|
|
214
217
|
|
|
215
218
|
return None
|
|
216
219
|
|
|
220
|
+
|
|
217
221
|
def unparse(module):
|
|
218
222
|
"""
|
|
219
223
|
Turn a module AST into python code
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import ast
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
from typing import Any, AnyStr, List, Optional, Text, Union
|
|
3
4
|
|
|
4
5
|
from .transforms.remove_annotations_options import RemoveAnnotationsOptions as RemoveAnnotationsOptions
|
|
5
6
|
|
|
7
|
+
|
|
6
8
|
class UnstableMinification(RuntimeError):
|
|
7
9
|
def __init__(self, exception: Any, source: Any, minified: Any): ...
|
|
8
10
|
|
|
11
|
+
|
|
9
12
|
def minify(
|
|
10
13
|
source: AnyStr,
|
|
11
14
|
filename: Optional[str] = ...,
|
|
@@ -28,8 +31,10 @@ def minify(
|
|
|
28
31
|
constant_folding: bool = ...
|
|
29
32
|
) -> Text: ...
|
|
30
33
|
|
|
34
|
+
|
|
31
35
|
def unparse(module: ast.Module) -> Text: ...
|
|
32
36
|
|
|
37
|
+
|
|
33
38
|
def awslambda(
|
|
34
39
|
source: AnyStr,
|
|
35
40
|
filename: Optional[Text] = ...,
|
|
@@ -7,14 +7,17 @@ import sys
|
|
|
7
7
|
from python_minifier import minify
|
|
8
8
|
from python_minifier.transforms.remove_annotations_options import RemoveAnnotationsOptions
|
|
9
9
|
|
|
10
|
+
|
|
10
11
|
if sys.version_info >= (3, 8):
|
|
11
12
|
from importlib import metadata
|
|
13
|
+
|
|
12
14
|
try:
|
|
13
15
|
version = metadata.version('python-minifier')
|
|
14
16
|
except metadata.PackageNotFoundError:
|
|
15
17
|
version = '0.0.0'
|
|
16
18
|
else:
|
|
17
|
-
from pkg_resources import
|
|
19
|
+
from pkg_resources import DistributionNotFound, get_distribution
|
|
20
|
+
|
|
18
21
|
try:
|
|
19
22
|
version = get_distribution('python_minifier').version
|
|
20
23
|
except DistributionNotFound:
|
|
@@ -170,25 +173,25 @@ def parse_args():
|
|
|
170
173
|
minification_options.add_argument(
|
|
171
174
|
'--no-preserve-shebang',
|
|
172
175
|
action='store_false',
|
|
173
|
-
help='
|
|
176
|
+
help='Disable preserving any shebang line from the source',
|
|
174
177
|
dest='preserve_shebang',
|
|
175
178
|
)
|
|
176
179
|
minification_options.add_argument(
|
|
177
180
|
'--remove-asserts',
|
|
178
181
|
action='store_true',
|
|
179
|
-
help='
|
|
182
|
+
help='Enable removing assert statements',
|
|
180
183
|
dest='remove_asserts',
|
|
181
184
|
)
|
|
182
185
|
minification_options.add_argument(
|
|
183
186
|
'--remove-debug',
|
|
184
187
|
action='store_true',
|
|
185
|
-
help='
|
|
188
|
+
help='Enable removing conditional statements that test __debug__ is True',
|
|
186
189
|
dest='remove_debug',
|
|
187
190
|
)
|
|
188
191
|
minification_options.add_argument(
|
|
189
192
|
'--no-remove-explicit-return-none',
|
|
190
193
|
action='store_false',
|
|
191
|
-
help='
|
|
194
|
+
help='Disable replacing explicit return None with a bare return',
|
|
192
195
|
dest='remove_explicit_return_none',
|
|
193
196
|
)
|
|
194
197
|
minification_options.add_argument(
|
|
@@ -268,9 +271,9 @@ def source_modules(args):
|
|
|
268
271
|
|
|
269
272
|
for path_arg in args.path:
|
|
270
273
|
if os.path.isdir(path_arg):
|
|
271
|
-
for root,
|
|
274
|
+
for root, _dirs, files in os.walk(path_arg, onerror=error, followlinks=True):
|
|
272
275
|
for file in files:
|
|
273
|
-
if file.endswith('.py'
|
|
276
|
+
if file.endswith(('.py', '.pyw')):
|
|
274
277
|
yield os.path.join(root, file)
|
|
275
278
|
else:
|
|
276
279
|
yield path_arg
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides utilities for annotating Abstract Syntax Tree (AST) nodes with parent references.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import ast
|
|
6
|
+
|
|
7
|
+
class _NoParent(ast.AST):
|
|
8
|
+
"""A placeholder class used to indicate that a node has no parent."""
|
|
9
|
+
|
|
10
|
+
def __repr__(self):
|
|
11
|
+
# type: () -> str
|
|
12
|
+
return 'NoParent()'
|
|
13
|
+
|
|
14
|
+
def add_parent(node, parent=_NoParent()):
|
|
15
|
+
# type: (ast.AST, ast.AST) -> None
|
|
16
|
+
"""
|
|
17
|
+
Recursively adds a parent reference to each node in the AST.
|
|
18
|
+
|
|
19
|
+
>>> tree = ast.parse('a = 1')
|
|
20
|
+
>>> add_parent(tree)
|
|
21
|
+
>>> get_parent(tree.body[0]) == tree
|
|
22
|
+
True
|
|
23
|
+
|
|
24
|
+
:param node: The current AST node.
|
|
25
|
+
:param parent: The parent :class:`ast.AST` node.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
node._parent = parent # type: ignore[attr-defined]
|
|
29
|
+
for child in ast.iter_child_nodes(node):
|
|
30
|
+
add_parent(child, node)
|
|
31
|
+
|
|
32
|
+
def get_parent(node):
|
|
33
|
+
# type: (ast.AST) -> ast.AST
|
|
34
|
+
"""
|
|
35
|
+
Retrieves the parent of the given AST node.
|
|
36
|
+
|
|
37
|
+
>>> tree = ast.parse('a = 1')
|
|
38
|
+
>>> add_parent(tree)
|
|
39
|
+
>>> get_parent(tree.body[0]) == tree
|
|
40
|
+
True
|
|
41
|
+
|
|
42
|
+
:param node: The AST node whose parent is to be retrieved.
|
|
43
|
+
:return: The parent AST node.
|
|
44
|
+
:raises ValueError: If the node has no parent.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
if not hasattr(node, '_parent') or isinstance(node._parent, _NoParent): # type: ignore[attr-defined]
|
|
48
|
+
raise ValueError('Node has no parent')
|
|
49
|
+
|
|
50
|
+
return node._parent # type: ignore[attr-defined]
|
|
51
|
+
|
|
52
|
+
def set_parent(node, parent):
|
|
53
|
+
# type: (ast.AST, ast.AST) -> None
|
|
54
|
+
"""
|
|
55
|
+
Replace the parent of the given AST node.
|
|
56
|
+
|
|
57
|
+
Create a simple AST:
|
|
58
|
+
>>> tree = ast.parse('a = func()')
|
|
59
|
+
>>> add_parent(tree)
|
|
60
|
+
>>> isinstance(tree.body[0], ast.Assign) and isinstance(tree.body[0].value, ast.Call)
|
|
61
|
+
True
|
|
62
|
+
>>> assign = tree.body[0]
|
|
63
|
+
>>> call = tree.body[0].value
|
|
64
|
+
>>> get_parent(call) == assign
|
|
65
|
+
True
|
|
66
|
+
|
|
67
|
+
Replace the parent of the call node:
|
|
68
|
+
>>> tree.body[0] = call
|
|
69
|
+
>>> set_parent(call, tree)
|
|
70
|
+
>>> get_parent(call) == tree
|
|
71
|
+
True
|
|
72
|
+
>>> from python_minifier.ast_printer import print_ast
|
|
73
|
+
>>> print(print_ast(tree))
|
|
74
|
+
Module(body=[
|
|
75
|
+
Call(Name('func'))
|
|
76
|
+
])
|
|
77
|
+
|
|
78
|
+
:param node: The AST node whose parent is to be set.
|
|
79
|
+
:param parent: The parent AST node.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
node._parent = parent # type: ignore[attr-defined]
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import python_minifier.ast_compat as ast
|
|
2
2
|
|
|
3
|
-
from python_minifier.util import is_ast_node
|
|
4
|
-
|
|
5
3
|
|
|
6
4
|
class CompareError(RuntimeError):
|
|
7
5
|
"""
|
|
@@ -18,7 +16,7 @@ class CompareError(RuntimeError):
|
|
|
18
16
|
|
|
19
17
|
def namespace(self, node):
|
|
20
18
|
if hasattr(node, 'namespace'):
|
|
21
|
-
if
|
|
19
|
+
if isinstance(node.namespace, (ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef)):
|
|
22
20
|
return self.namespace(node.namespace) + '.' + node.namespace.name
|
|
23
21
|
elif isinstance(node.namespace, ast.Module):
|
|
24
22
|
return ''
|
|
@@ -8,6 +8,7 @@ deprecation warnings.
|
|
|
8
8
|
|
|
9
9
|
from ast import *
|
|
10
10
|
|
|
11
|
+
|
|
11
12
|
# Ideally we don't import anything else
|
|
12
13
|
|
|
13
14
|
if 'TypeAlias' in globals():
|
|
@@ -16,6 +17,7 @@ if 'TypeAlias' in globals():
|
|
|
16
17
|
Constant.n = property(lambda self: self.value, lambda self, value: setattr(self, 'value', value)) # type: ignore[assignment]
|
|
17
18
|
Constant.s = property(lambda self: self.value, lambda self, value: setattr(self, 'value', value)) # type: ignore[assignment]
|
|
18
19
|
|
|
20
|
+
|
|
19
21
|
# These classes are redefined from the ones in ast that complain about deprecation
|
|
20
22
|
# They will continue to work once they are removed from ast
|
|
21
23
|
|
|
@@ -23,18 +25,54 @@ if 'TypeAlias' in globals():
|
|
|
23
25
|
def __new__(cls, s, *args, **kwargs):
|
|
24
26
|
return Constant(value=s, *args, **kwargs)
|
|
25
27
|
|
|
28
|
+
|
|
26
29
|
class Bytes(Constant): # type: ignore[no-redef]
|
|
27
30
|
def __new__(cls, s, *args, **kwargs):
|
|
28
31
|
return Constant(value=s, *args, **kwargs)
|
|
29
32
|
|
|
33
|
+
|
|
30
34
|
class Num(Constant): # type: ignore[no-redef]
|
|
31
35
|
def __new__(cls, n, *args, **kwargs):
|
|
32
36
|
return Constant(value=n, *args, **kwargs)
|
|
33
37
|
|
|
38
|
+
|
|
34
39
|
class NameConstant(Constant): # type: ignore[no-redef]
|
|
35
40
|
def __new__(cls, *args, **kwargs):
|
|
36
41
|
return Constant(*args, **kwargs)
|
|
37
42
|
|
|
43
|
+
|
|
38
44
|
class Ellipsis(Constant): # type: ignore[no-redef]
|
|
39
45
|
def __new__(cls, *args, **kwargs):
|
|
40
46
|
return Constant(value=literal_eval('...'), *args, **kwargs)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# Create a dummy class for missing AST nodes
|
|
50
|
+
for _node_type in [
|
|
51
|
+
'AnnAssign',
|
|
52
|
+
'AsyncFor',
|
|
53
|
+
'AsyncFunctionDef',
|
|
54
|
+
'AsyncFunctionDef',
|
|
55
|
+
'AsyncWith',
|
|
56
|
+
'Bytes',
|
|
57
|
+
'Constant',
|
|
58
|
+
'DictComp',
|
|
59
|
+
'Exec',
|
|
60
|
+
'ListComp',
|
|
61
|
+
'MatchAs',
|
|
62
|
+
'MatchMapping',
|
|
63
|
+
'MatchStar',
|
|
64
|
+
'NameConstant',
|
|
65
|
+
'NamedExpr',
|
|
66
|
+
'Nonlocal',
|
|
67
|
+
'ParamSpec',
|
|
68
|
+
'SetComp',
|
|
69
|
+
'Starred',
|
|
70
|
+
'TryStar',
|
|
71
|
+
'TypeVar',
|
|
72
|
+
'TypeVarTuple',
|
|
73
|
+
'YieldFrom',
|
|
74
|
+
'arg',
|
|
75
|
+
'withitem',
|
|
76
|
+
]:
|
|
77
|
+
if _node_type not in globals():
|
|
78
|
+
globals()[_node_type] = type(_node_type, (AST,), {})
|
|
@@ -12,7 +12,8 @@ fields or field names may be omitted for clarity. It should still be precise and
|
|
|
12
12
|
|
|
13
13
|
import python_minifier.ast_compat as ast
|
|
14
14
|
|
|
15
|
-
from python_minifier.util import
|
|
15
|
+
from python_minifier.util import is_constant_node
|
|
16
|
+
|
|
16
17
|
|
|
17
18
|
INDENT = ' '
|
|
18
19
|
|
|
@@ -63,24 +64,26 @@ default_fields = {
|
|
|
63
64
|
'AugAssign': 'op',
|
|
64
65
|
}
|
|
65
66
|
|
|
67
|
+
|
|
66
68
|
def is_literal(node, field):
|
|
67
69
|
if hasattr(ast, 'Constant') and isinstance(node, ast.Constant) and field == 'value':
|
|
68
70
|
return True
|
|
69
71
|
|
|
70
|
-
if
|
|
72
|
+
if is_constant_node(node, ast.Num) and field == 'n':
|
|
71
73
|
return True
|
|
72
74
|
|
|
73
|
-
if
|
|
75
|
+
if is_constant_node(node, ast.Str) and field == 's':
|
|
74
76
|
return True
|
|
75
77
|
|
|
76
|
-
if
|
|
78
|
+
if is_constant_node(node, ast.Bytes) and field == 's':
|
|
77
79
|
return True
|
|
78
80
|
|
|
79
|
-
if
|
|
81
|
+
if is_constant_node(node, ast.NameConstant) and field == 'value':
|
|
80
82
|
return True
|
|
81
83
|
|
|
82
84
|
return False
|
|
83
85
|
|
|
86
|
+
|
|
84
87
|
def print_ast(node):
|
|
85
88
|
if not isinstance(node, ast.AST):
|
|
86
89
|
return repr(node)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import python_minifier.ast_compat as ast
|
|
2
1
|
import sys
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
import python_minifier.ast_compat as ast
|
|
5
4
|
|
|
6
|
-
from python_minifier.token_printer import
|
|
5
|
+
from python_minifier.token_printer import Delimiter, TokenPrinter
|
|
6
|
+
from python_minifier.util import is_constant_node
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class ExpressionPrinter(object):
|
|
@@ -69,7 +69,7 @@ class ExpressionPrinter(object):
|
|
|
69
69
|
|
|
70
70
|
# Python2 parses negative ints as an ast.Num with a negative value.
|
|
71
71
|
# Make sure the Num get the precedence of the USub operator in this case.
|
|
72
|
-
if sys.version_info < (3, 0) and
|
|
72
|
+
if sys.version_info < (3, 0) and is_constant_node(node, ast.Num):
|
|
73
73
|
if str(node.n)[0] == '-':
|
|
74
74
|
return self.precedences['USub']
|
|
75
75
|
|
|
@@ -156,7 +156,7 @@ class ExpressionPrinter(object):
|
|
|
156
156
|
if key is None:
|
|
157
157
|
self.printer.operator('**')
|
|
158
158
|
|
|
159
|
-
if 0 < self.precedence(datum) <=7:
|
|
159
|
+
if 0 < self.precedence(datum) <= 7:
|
|
160
160
|
self.printer.delimiter('(')
|
|
161
161
|
self._expression(datum)
|
|
162
162
|
self.printer.delimiter(')')
|
|
@@ -208,7 +208,7 @@ class ExpressionPrinter(object):
|
|
|
208
208
|
def visit_UnaryOp(self, node):
|
|
209
209
|
self.visit(node.op)
|
|
210
210
|
|
|
211
|
-
if sys.version_info < (3, 0) and isinstance(node.op, ast.USub) and
|
|
211
|
+
if sys.version_info < (3, 0) and isinstance(node.op, ast.USub) and is_constant_node(node.operand, ast.Num):
|
|
212
212
|
# For: -(1), which is parsed as a UnaryOp(USub, Num(1)).
|
|
213
213
|
# Without this special case it would be printed as -1
|
|
214
214
|
# This is fine, but python 2 will then parse it at Num(-1) so the AST wouldn't round-trip.
|
|
@@ -428,7 +428,7 @@ class ExpressionPrinter(object):
|
|
|
428
428
|
value_precedence = self.precedence(node.value)
|
|
429
429
|
attr_precedence = self.precedence(node)
|
|
430
430
|
|
|
431
|
-
if (value_precedence != 0 and (attr_precedence > value_precedence)) or
|
|
431
|
+
if (value_precedence != 0 and (attr_precedence > value_precedence)) or is_constant_node(node.value, ast.Num):
|
|
432
432
|
self.printer.delimiter('(')
|
|
433
433
|
self._expression(node.value)
|
|
434
434
|
self.printer.delimiter(')')
|
|
@@ -462,7 +462,7 @@ class ExpressionPrinter(object):
|
|
|
462
462
|
self.visit_Slice(node.slice)
|
|
463
463
|
elif isinstance(node.slice, ast.ExtSlice):
|
|
464
464
|
self.visit_ExtSlice(node.slice)
|
|
465
|
-
elif
|
|
465
|
+
elif is_constant_node(node.slice, ast.Ellipsis):
|
|
466
466
|
self.visit_Ellipsis(node)
|
|
467
467
|
elif sys.version_info >= (3, 9) and isinstance(node.slice, ast.Tuple):
|
|
468
468
|
self.visit_Tuple(node.slice)
|
|
@@ -629,7 +629,8 @@ class ExpressionPrinter(object):
|
|
|
629
629
|
def visit_arg(self, node):
|
|
630
630
|
if isinstance(node, ast.Name):
|
|
631
631
|
# Python 2 uses Name nodes
|
|
632
|
-
|
|
632
|
+
self.visit_Name(node)
|
|
633
|
+
return
|
|
633
634
|
|
|
634
635
|
self.printer.identifier(node.arg)
|
|
635
636
|
|
|
@@ -648,7 +649,7 @@ class ExpressionPrinter(object):
|
|
|
648
649
|
self._expression(node.body)
|
|
649
650
|
|
|
650
651
|
def _expression(self, expression):
|
|
651
|
-
if
|
|
652
|
+
if isinstance(expression, (ast.Yield, ast.YieldFrom)):
|
|
652
653
|
self.printer.delimiter('(')
|
|
653
654
|
self._yield_expr(expression)
|
|
654
655
|
self.printer.delimiter(')')
|
|
@@ -656,7 +657,7 @@ class ExpressionPrinter(object):
|
|
|
656
657
|
self.printer.delimiter('(')
|
|
657
658
|
self.visit_Tuple(expression)
|
|
658
659
|
self.printer.delimiter(')')
|
|
659
|
-
elif
|
|
660
|
+
elif isinstance(expression, ast.NamedExpr):
|
|
660
661
|
self.printer.delimiter('(')
|
|
661
662
|
self.visit_NamedExpr(expression)
|
|
662
663
|
self.printer.delimiter(')')
|
|
@@ -664,11 +665,11 @@ class ExpressionPrinter(object):
|
|
|
664
665
|
self.visit(expression)
|
|
665
666
|
|
|
666
667
|
def _testlist(self, test):
|
|
667
|
-
if
|
|
668
|
+
if isinstance(test, (ast.Yield, ast.YieldFrom)):
|
|
668
669
|
self.printer.delimiter('(')
|
|
669
670
|
self._yield_expr(test)
|
|
670
671
|
self.printer.delimiter(')')
|
|
671
|
-
elif
|
|
672
|
+
elif isinstance(test, ast.NamedExpr):
|
|
672
673
|
self.printer.delimiter('(')
|
|
673
674
|
self.visit_NamedExpr(test)
|
|
674
675
|
self.printer.delimiter(')')
|
|
@@ -6,17 +6,17 @@ Mostly because FStrings feel like a hack.
|
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
import python_minifier.ast_compat as ast
|
|
10
9
|
import copy
|
|
11
10
|
import re
|
|
12
11
|
|
|
12
|
+
import python_minifier.ast_compat as ast
|
|
13
|
+
|
|
13
14
|
from python_minifier import UnstableMinification
|
|
14
|
-
from python_minifier.ast_compare import CompareError
|
|
15
|
-
from python_minifier.ast_compare import compare_ast
|
|
15
|
+
from python_minifier.ast_compare import CompareError, compare_ast
|
|
16
16
|
from python_minifier.expression_printer import ExpressionPrinter
|
|
17
17
|
from python_minifier.ministring import MiniString
|
|
18
18
|
from python_minifier.token_printer import TokenTypes
|
|
19
|
-
from python_minifier.util import
|
|
19
|
+
from python_minifier.util import is_constant_node
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class FString(object):
|
|
@@ -36,7 +36,7 @@ class FString(object):
|
|
|
36
36
|
c = ast.parse(code, 'FString candidate', mode='eval')
|
|
37
37
|
compare_ast(self.node, c.body)
|
|
38
38
|
return True
|
|
39
|
-
except Exception
|
|
39
|
+
except Exception:
|
|
40
40
|
return False
|
|
41
41
|
|
|
42
42
|
def complete_debug_specifier(self, partial_specifier_candidates, value_node):
|
|
@@ -70,7 +70,7 @@ class FString(object):
|
|
|
70
70
|
nested_allowed.remove(quote)
|
|
71
71
|
|
|
72
72
|
for v in self.node.values:
|
|
73
|
-
if
|
|
73
|
+
if is_constant_node(v, ast.Str):
|
|
74
74
|
|
|
75
75
|
# Could this be used as a debug specifier?
|
|
76
76
|
if len(candidates) < 10:
|
|
@@ -79,12 +79,12 @@ class FString(object):
|
|
|
79
79
|
# Maybe!
|
|
80
80
|
try:
|
|
81
81
|
debug_specifier_candidates = [x + '{' + v.s for x in candidates]
|
|
82
|
-
except Exception
|
|
82
|
+
except Exception:
|
|
83
83
|
continue
|
|
84
84
|
|
|
85
85
|
try:
|
|
86
86
|
candidates = [x + self.str_for(v.s, quote) for x in candidates]
|
|
87
|
-
except Exception
|
|
87
|
+
except Exception:
|
|
88
88
|
continue
|
|
89
89
|
elif isinstance(v, ast.FormattedValue):
|
|
90
90
|
try:
|
|
@@ -93,15 +93,14 @@ class FString(object):
|
|
|
93
93
|
x + y for x in candidates for y in FormattedValue(v, nested_allowed, self.pep701).get_candidates()
|
|
94
94
|
] + completed
|
|
95
95
|
debug_specifier_candidates = []
|
|
96
|
-
except Exception
|
|
96
|
+
except Exception:
|
|
97
97
|
continue
|
|
98
98
|
else:
|
|
99
99
|
raise RuntimeError('Unexpected JoinedStr value')
|
|
100
100
|
|
|
101
101
|
actual_candidates += ['f' + quote + x + quote for x in candidates]
|
|
102
102
|
|
|
103
|
-
|
|
104
|
-
return actual_candidates
|
|
103
|
+
return filter(self.is_correct_ast, actual_candidates)
|
|
105
104
|
|
|
106
105
|
def str_for(self, s, quote):
|
|
107
106
|
return s.replace('{', '{{').replace('}', '}}')
|
|
@@ -270,7 +269,7 @@ class Str(object):
|
|
|
270
269
|
elif c != quote:
|
|
271
270
|
return quote
|
|
272
271
|
|
|
273
|
-
raise ValueError(
|
|
272
|
+
raise ValueError("Couldn't find a quote")
|
|
274
273
|
|
|
275
274
|
def _literals(self):
|
|
276
275
|
l = ''
|
|
@@ -349,7 +348,7 @@ class FormatSpec(object):
|
|
|
349
348
|
|
|
350
349
|
candidates = ['']
|
|
351
350
|
for v in self.node.values:
|
|
352
|
-
if
|
|
351
|
+
if is_constant_node(v, ast.Str):
|
|
353
352
|
candidates = [x + self.str_for(v.s) for x in candidates]
|
|
354
353
|
elif isinstance(v, ast.FormattedValue):
|
|
355
354
|
candidates = [
|
|
@@ -397,7 +396,7 @@ class Bytes(object):
|
|
|
397
396
|
elif chr(c) != quote:
|
|
398
397
|
return quote
|
|
399
398
|
|
|
400
|
-
raise ValueError(
|
|
399
|
+
raise ValueError("Couldn't find a quote")
|
|
401
400
|
|
|
402
401
|
def _literals(self):
|
|
403
402
|
l = ''
|
|
@@ -32,7 +32,7 @@ class MiniString(object):
|
|
|
32
32
|
|
|
33
33
|
try:
|
|
34
34
|
eval(self.quote + s + self.quote)
|
|
35
|
-
except (UnicodeDecodeError, UnicodeEncodeError)
|
|
35
|
+
except (UnicodeDecodeError, UnicodeEncodeError):
|
|
36
36
|
if self.safe_mode:
|
|
37
37
|
raise
|
|
38
38
|
|
|
@@ -63,7 +63,7 @@ class MiniString(object):
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
for c in self._s:
|
|
66
|
-
if c in escaped
|
|
66
|
+
if c in escaped:
|
|
67
67
|
s += escaped[c]
|
|
68
68
|
else:
|
|
69
69
|
if self.safe_mode:
|
|
@@ -95,7 +95,7 @@ class MiniString(object):
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
for c in self._s:
|
|
98
|
-
if c in escaped
|
|
98
|
+
if c in escaped:
|
|
99
99
|
s += escaped[c]
|
|
100
100
|
else:
|
|
101
101
|
if self.safe_mode:
|