python-minifier 2.11.2__py2-none-any.whl
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/__init__.py +269 -0
- python_minifier/__init__.pyi +37 -0
- python_minifier/__main__.py +332 -0
- python_minifier/ast_compare.py +104 -0
- python_minifier/ast_compat.py +40 -0
- python_minifier/ast_printer.py +130 -0
- python_minifier/expression_printer.py +753 -0
- python_minifier/f_string.py +446 -0
- python_minifier/ministring.py +179 -0
- python_minifier/module_printer.py +862 -0
- python_minifier/py.typed +0 -0
- python_minifier/rename/__init__.py +6 -0
- python_minifier/rename/bind_names.py +192 -0
- python_minifier/rename/binding.py +495 -0
- python_minifier/rename/mapper.py +175 -0
- python_minifier/rename/name_generator.py +51 -0
- python_minifier/rename/rename_literals.py +249 -0
- python_minifier/rename/renamer.py +230 -0
- python_minifier/rename/resolve_names.py +106 -0
- python_minifier/rename/util.py +203 -0
- python_minifier/token_printer.py +299 -0
- python_minifier/transforms/__init__.py +0 -0
- python_minifier/transforms/combine_imports.py +76 -0
- python_minifier/transforms/constant_folding.py +114 -0
- python_minifier/transforms/remove_annotations.py +130 -0
- python_minifier/transforms/remove_annotations_options.py +35 -0
- python_minifier/transforms/remove_annotations_options.pyi +17 -0
- python_minifier/transforms/remove_asserts.py +26 -0
- python_minifier/transforms/remove_debug.py +53 -0
- python_minifier/transforms/remove_exception_brackets.py +124 -0
- python_minifier/transforms/remove_explicit_return_none.py +38 -0
- python_minifier/transforms/remove_literal_statements.py +61 -0
- python_minifier/transforms/remove_object_base.py +24 -0
- python_minifier/transforms/remove_pass.py +26 -0
- python_minifier/transforms/remove_posargs.py +13 -0
- python_minifier/transforms/suite_transformer.py +196 -0
- python_minifier/util.py +48 -0
- python_minifier-2.11.2.dist-info/LICENSE +21 -0
- python_minifier-2.11.2.dist-info/METADATA +177 -0
- python_minifier-2.11.2.dist-info/RECORD +44 -0
- python_minifier-2.11.2.dist-info/WHEEL +5 -0
- python_minifier-2.11.2.dist-info/entry_points.txt +3 -0
- python_minifier-2.11.2.dist-info/top_level.txt +1 -0
- python_minifier-2.11.2.dist-info/zip-safe +1 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import python_minifier.ast_compat as ast
|
|
2
|
+
|
|
3
|
+
from python_minifier.util import is_ast_node
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CompareError(RuntimeError):
|
|
7
|
+
"""
|
|
8
|
+
Raised when an AST compares unequal.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, lnode, rnode, msg=None):
|
|
12
|
+
self.lnode = lnode
|
|
13
|
+
self.rnode = rnode
|
|
14
|
+
self.msg = msg
|
|
15
|
+
|
|
16
|
+
def __repr__(self):
|
|
17
|
+
return 'NodeError(%r, %r)' % (self.lnode, self.rnode)
|
|
18
|
+
|
|
19
|
+
def namespace(self, node):
|
|
20
|
+
if hasattr(node, 'namespace'):
|
|
21
|
+
if is_ast_node(node.namespace, (ast.FunctionDef, ast.ClassDef, 'AsyncFunctionDef')):
|
|
22
|
+
return self.namespace(node.namespace) + '.' + node.namespace.name
|
|
23
|
+
elif isinstance(node.namespace, ast.Module):
|
|
24
|
+
return ''
|
|
25
|
+
else:
|
|
26
|
+
return repr(node.namespace.__class__)
|
|
27
|
+
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
def __str__(self):
|
|
31
|
+
error = ''
|
|
32
|
+
|
|
33
|
+
if self.msg:
|
|
34
|
+
error += self.msg
|
|
35
|
+
|
|
36
|
+
if self.namespace(self.lnode):
|
|
37
|
+
error += ' in namespace ' + self.namespace(self.lnode)
|
|
38
|
+
|
|
39
|
+
if self.lnode and hasattr(self.lnode, 'lineno'):
|
|
40
|
+
error += ' at source %i:%i' % (self.lnode.lineno, self.lnode.col_offset)
|
|
41
|
+
|
|
42
|
+
return error
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def compare_ast(l_ast, r_ast):
|
|
46
|
+
"""
|
|
47
|
+
Compare Python Abstract Syntax Trees
|
|
48
|
+
|
|
49
|
+
>>> compare_ast(l_ast, r_ast)
|
|
50
|
+
|
|
51
|
+
If the AST's are not identical, an exception will be raised.
|
|
52
|
+
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def counter():
|
|
56
|
+
i = 0
|
|
57
|
+
while True:
|
|
58
|
+
yield i
|
|
59
|
+
i += 1
|
|
60
|
+
|
|
61
|
+
if type(l_ast) != type(r_ast):
|
|
62
|
+
raise CompareError(l_ast, r_ast, msg='Nodes do not match! %r != %r' % (l_ast, r_ast))
|
|
63
|
+
|
|
64
|
+
for field in set(l_ast._fields + r_ast._fields):
|
|
65
|
+
|
|
66
|
+
if field == 'kind' and isinstance(l_ast, ast.Constant):
|
|
67
|
+
continue
|
|
68
|
+
|
|
69
|
+
if isinstance(getattr(l_ast, field, None), list):
|
|
70
|
+
|
|
71
|
+
l_list = getattr(l_ast, field, None)
|
|
72
|
+
r_list = getattr(r_ast, field, None)
|
|
73
|
+
|
|
74
|
+
if len(l_list) != len(r_list):
|
|
75
|
+
raise CompareError(
|
|
76
|
+
l_list,
|
|
77
|
+
r_list,
|
|
78
|
+
'List does not have the same number of elements! len(%s.%s)=%r, len(%s.%s)=%r'
|
|
79
|
+
% (type(l_ast), field, len(l_list), type(r_ast), field, len(r_list)),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
for i, l, r in zip(counter(), l_list, r_list):
|
|
83
|
+
if isinstance(l, ast.AST) or isinstance(r, ast.AST):
|
|
84
|
+
compare_ast(l, r)
|
|
85
|
+
elif l != r:
|
|
86
|
+
raise CompareError(
|
|
87
|
+
l_ast,
|
|
88
|
+
r_ast,
|
|
89
|
+
'Fields do not match! %s.%s[%i]=%r, %s.%s[%i]=%r'
|
|
90
|
+
% (type(l_ast), field, i, l, type(r_ast), field, i, r),
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
else:
|
|
94
|
+
l = getattr(l_ast, field, None)
|
|
95
|
+
r = getattr(r_ast, field, None)
|
|
96
|
+
|
|
97
|
+
if isinstance(l, ast.AST) or isinstance(r, ast.AST):
|
|
98
|
+
compare_ast(l, r)
|
|
99
|
+
elif l != r:
|
|
100
|
+
raise CompareError(
|
|
101
|
+
l_ast,
|
|
102
|
+
r_ast,
|
|
103
|
+
'Fields do not match! %s.%s=%r, %s.%s=%r' % (type(l_ast), field, l, type(r_ast), field, r),
|
|
104
|
+
)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The is a backwards compatible shim for the ast module.
|
|
3
|
+
|
|
4
|
+
This is the best way to make the ast module work the same in both python 2 and 3.
|
|
5
|
+
This is essentially what the ast module was doing until 3.12, when it started throwing
|
|
6
|
+
deprecation warnings.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from ast import *
|
|
10
|
+
|
|
11
|
+
# Ideally we don't import anything else
|
|
12
|
+
|
|
13
|
+
if 'TypeAlias' in globals():
|
|
14
|
+
|
|
15
|
+
# Add n and s properties to Constant so it can stand in for Num, Str and Bytes
|
|
16
|
+
Constant.n = property(lambda self: self.value, lambda self, value: setattr(self, 'value', value)) # type: ignore[assignment]
|
|
17
|
+
Constant.s = property(lambda self: self.value, lambda self, value: setattr(self, 'value', value)) # type: ignore[assignment]
|
|
18
|
+
|
|
19
|
+
# These classes are redefined from the ones in ast that complain about deprecation
|
|
20
|
+
# They will continue to work once they are removed from ast
|
|
21
|
+
|
|
22
|
+
class Str(Constant): # type: ignore[no-redef]
|
|
23
|
+
def __new__(cls, s, *args, **kwargs):
|
|
24
|
+
return Constant(value=s, *args, **kwargs)
|
|
25
|
+
|
|
26
|
+
class Bytes(Constant): # type: ignore[no-redef]
|
|
27
|
+
def __new__(cls, s, *args, **kwargs):
|
|
28
|
+
return Constant(value=s, *args, **kwargs)
|
|
29
|
+
|
|
30
|
+
class Num(Constant): # type: ignore[no-redef]
|
|
31
|
+
def __new__(cls, n, *args, **kwargs):
|
|
32
|
+
return Constant(value=n, *args, **kwargs)
|
|
33
|
+
|
|
34
|
+
class NameConstant(Constant): # type: ignore[no-redef]
|
|
35
|
+
def __new__(cls, *args, **kwargs):
|
|
36
|
+
return Constant(*args, **kwargs)
|
|
37
|
+
|
|
38
|
+
class Ellipsis(Constant): # type: ignore[no-redef]
|
|
39
|
+
def __new__(cls, *args, **kwargs):
|
|
40
|
+
return Constant(value=literal_eval('...'), *args, **kwargs)
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Print a representation of an AST
|
|
3
|
+
|
|
4
|
+
This prints a human readable representation of the nodes in the AST.
|
|
5
|
+
The goal is to make it easy to see what the AST looks like, and to
|
|
6
|
+
make it easy to compare two ASTs.
|
|
7
|
+
|
|
8
|
+
This is not intended to be a complete representation of the AST, some
|
|
9
|
+
fields or field names may be omitted for clarity. It should still be precise and unambiguous.
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import python_minifier.ast_compat as ast
|
|
14
|
+
|
|
15
|
+
from python_minifier.util import is_ast_node
|
|
16
|
+
|
|
17
|
+
INDENT = ' '
|
|
18
|
+
|
|
19
|
+
# The field name that can be omitted for each node
|
|
20
|
+
# Either it's the only field or would otherwise be obvious
|
|
21
|
+
default_fields = {
|
|
22
|
+
'Constant': 'value',
|
|
23
|
+
'Num': 'n',
|
|
24
|
+
'Str': 's',
|
|
25
|
+
'Bytes': 's',
|
|
26
|
+
'NameConstant': 'value',
|
|
27
|
+
'FormattedValue': 'value',
|
|
28
|
+
'JoinedStr': 'values',
|
|
29
|
+
'List': 'elts',
|
|
30
|
+
'Tuple': 'elts',
|
|
31
|
+
'Set': 'elts',
|
|
32
|
+
'Name': 'id',
|
|
33
|
+
'Expr': 'value',
|
|
34
|
+
'UnaryOp': 'op',
|
|
35
|
+
'BinOp': 'op',
|
|
36
|
+
'BoolOp': 'op',
|
|
37
|
+
'Call': 'func',
|
|
38
|
+
'Index': 'value',
|
|
39
|
+
'ExtSlice': 'dims',
|
|
40
|
+
'Assert': 'test',
|
|
41
|
+
'Delete': 'targets',
|
|
42
|
+
'Import': 'names',
|
|
43
|
+
'If': 'test',
|
|
44
|
+
'While': 'test',
|
|
45
|
+
'Try': 'handlers',
|
|
46
|
+
'TryExcept': 'handlers',
|
|
47
|
+
'With': 'items',
|
|
48
|
+
'withitem': 'context_expr',
|
|
49
|
+
'FunctionDef': 'name',
|
|
50
|
+
'arg': 'arg',
|
|
51
|
+
'Return': 'value',
|
|
52
|
+
'Yield': 'value',
|
|
53
|
+
'YieldFrom': 'value',
|
|
54
|
+
'Global': 'names',
|
|
55
|
+
'Nonlocal': 'names',
|
|
56
|
+
'ClassDef': 'name',
|
|
57
|
+
'AsyncFunctionDef': 'name',
|
|
58
|
+
'Await': 'value',
|
|
59
|
+
'AsyncWith': 'items',
|
|
60
|
+
'Raise': 'exc',
|
|
61
|
+
'Subscript': 'value',
|
|
62
|
+
'Attribute': 'value',
|
|
63
|
+
'AugAssign': 'op',
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
def is_literal(node, field):
|
|
67
|
+
if hasattr(ast, 'Constant') and isinstance(node, ast.Constant) and field == 'value':
|
|
68
|
+
return True
|
|
69
|
+
|
|
70
|
+
if is_ast_node(node, ast.Num) and field == 'n':
|
|
71
|
+
return True
|
|
72
|
+
|
|
73
|
+
if is_ast_node(node, ast.Str) and field == 's':
|
|
74
|
+
return True
|
|
75
|
+
|
|
76
|
+
if is_ast_node(node, 'Bytes') and field == 's':
|
|
77
|
+
return True
|
|
78
|
+
|
|
79
|
+
if is_ast_node(node, 'NameConstant') and field == 'value':
|
|
80
|
+
return True
|
|
81
|
+
|
|
82
|
+
return False
|
|
83
|
+
|
|
84
|
+
def print_ast(node):
|
|
85
|
+
if not isinstance(node, ast.AST):
|
|
86
|
+
return repr(node)
|
|
87
|
+
|
|
88
|
+
s = ''
|
|
89
|
+
|
|
90
|
+
node_name = node.__class__.__name__
|
|
91
|
+
s += node_name
|
|
92
|
+
s += '('
|
|
93
|
+
|
|
94
|
+
first = True
|
|
95
|
+
for field, value in ast.iter_fields(node):
|
|
96
|
+
if not value and not is_literal(node, field):
|
|
97
|
+
# Don't bother printing fields that are empty, except for literals
|
|
98
|
+
continue
|
|
99
|
+
|
|
100
|
+
if field == 'ctx':
|
|
101
|
+
# Don't print the ctx, it's always apparent from context
|
|
102
|
+
continue
|
|
103
|
+
|
|
104
|
+
if first:
|
|
105
|
+
first = False
|
|
106
|
+
else:
|
|
107
|
+
s += ', '
|
|
108
|
+
|
|
109
|
+
if default_fields.get(node_name) != field:
|
|
110
|
+
s += field + '='
|
|
111
|
+
|
|
112
|
+
if isinstance(value, ast.AST):
|
|
113
|
+
s += print_ast(value)
|
|
114
|
+
elif isinstance(value, list):
|
|
115
|
+
s += '['
|
|
116
|
+
first_list = True
|
|
117
|
+
for item in value:
|
|
118
|
+
if first_list:
|
|
119
|
+
first_list = False
|
|
120
|
+
else:
|
|
121
|
+
s += ','
|
|
122
|
+
|
|
123
|
+
for line in print_ast(item).splitlines():
|
|
124
|
+
s += '\n' + INDENT + line
|
|
125
|
+
s += '\n]'
|
|
126
|
+
else:
|
|
127
|
+
s += repr(value)
|
|
128
|
+
|
|
129
|
+
s += ')'
|
|
130
|
+
return s
|