python-minifier 2.4.2__py3-none-any.whl → 2.10.0__py3-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 +88 -6
- python_minifier/__init__.pyi +37 -0
- python_minifier/__main__.py +255 -51
- python_minifier/ast_compare.py +4 -4
- python_minifier/ast_compat.py +40 -0
- python_minifier/ast_printer.py +130 -0
- python_minifier/expression_printer.py +224 -273
- python_minifier/f_string.py +92 -36
- python_minifier/module_printer.py +401 -235
- python_minifier/py.typed +0 -0
- python_minifier/rename/bind_names.py +64 -25
- python_minifier/rename/binding.py +237 -33
- python_minifier/rename/mapper.py +41 -12
- python_minifier/rename/rename_literals.py +45 -11
- python_minifier/rename/renamer.py +2 -2
- python_minifier/rename/resolve_names.py +57 -3
- python_minifier/rename/util.py +17 -16
- python_minifier/token_printer.py +299 -0
- python_minifier/transforms/combine_imports.py +8 -5
- python_minifier/transforms/constant_folding.py +114 -0
- python_minifier/transforms/remove_annotations.py +29 -5
- 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 +3 -2
- python_minifier/transforms/remove_object_base.py +4 -1
- python_minifier/transforms/remove_pass.py +3 -2
- python_minifier/transforms/remove_posargs.py +1 -1
- python_minifier/transforms/suite_transformer.py +6 -51
- python_minifier/util.py +48 -0
- {python_minifier-2.4.2.dist-info → python_minifier-2.10.0.dist-info}/METADATA +9 -9
- python_minifier-2.10.0.dist-info/RECORD +44 -0
- {python_minifier-2.4.2.dist-info → python_minifier-2.10.0.dist-info}/WHEEL +1 -1
- {python_minifier-2.4.2.dist-info → python_minifier-2.10.0.dist-info}/entry_points.txt +0 -1
- python_minifier-2.4.2.dist-info/RECORD +0 -31
- {python_minifier-2.4.2.dist-info → python_minifier-2.10.0.dist-info}/LICENSE +0 -0
- {python_minifier-2.4.2.dist-info → python_minifier-2.10.0.dist-info}/top_level.txt +0 -0
- {python_minifier-2.4.2.dist-info → python_minifier-2.10.0.dist-info}/zip-safe +0 -0
python_minifier/__init__.py
CHANGED
|
@@ -4,7 +4,8 @@ a 'minified' representation of the same source code.
|
|
|
4
4
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
import ast
|
|
7
|
+
import python_minifier.ast_compat as ast
|
|
8
|
+
import re
|
|
8
9
|
|
|
9
10
|
from python_minifier.ast_compare import CompareError, compare_ast
|
|
10
11
|
from python_minifier.module_printer import ModulePrinter
|
|
@@ -17,8 +18,15 @@ from python_minifier.rename import (
|
|
|
17
18
|
allow_rename_locals,
|
|
18
19
|
add_namespace,
|
|
19
20
|
)
|
|
21
|
+
|
|
20
22
|
from python_minifier.transforms.combine_imports import CombineImports
|
|
23
|
+
from python_minifier.transforms.constant_folding import FoldConstants
|
|
21
24
|
from python_minifier.transforms.remove_annotations import RemoveAnnotations
|
|
25
|
+
from python_minifier.transforms.remove_annotations_options import RemoveAnnotationsOptions
|
|
26
|
+
from python_minifier.transforms.remove_asserts import RemoveAsserts
|
|
27
|
+
from python_minifier.transforms.remove_debug import RemoveDebug
|
|
28
|
+
from python_minifier.transforms.remove_explicit_return_none import RemoveExplicitReturnNone
|
|
29
|
+
from python_minifier.transforms.remove_exception_brackets import remove_no_arg_exception_call
|
|
22
30
|
from python_minifier.transforms.remove_literal_statements import RemoveLiteralStatements
|
|
23
31
|
from python_minifier.transforms.remove_object_base import RemoveObject
|
|
24
32
|
from python_minifier.transforms.remove_pass import RemovePass
|
|
@@ -47,7 +55,7 @@ class UnstableMinification(RuntimeError):
|
|
|
47
55
|
def minify(
|
|
48
56
|
source,
|
|
49
57
|
filename=None,
|
|
50
|
-
remove_annotations=
|
|
58
|
+
remove_annotations=RemoveAnnotationsOptions(),
|
|
51
59
|
remove_pass=True,
|
|
52
60
|
remove_literal_statements=False,
|
|
53
61
|
combine_imports=True,
|
|
@@ -58,6 +66,12 @@ def minify(
|
|
|
58
66
|
preserve_globals=None,
|
|
59
67
|
remove_object_base=True,
|
|
60
68
|
convert_posargs_to_args=True,
|
|
69
|
+
preserve_shebang=True,
|
|
70
|
+
remove_asserts=False,
|
|
71
|
+
remove_debug=False,
|
|
72
|
+
remove_explicit_return_none=True,
|
|
73
|
+
remove_builtin_exception_brackets=True,
|
|
74
|
+
constant_folding=True
|
|
61
75
|
):
|
|
62
76
|
"""
|
|
63
77
|
Minify a python module
|
|
@@ -71,7 +85,8 @@ def minify(
|
|
|
71
85
|
:param str source: The python module source code
|
|
72
86
|
:param str filename: The original source filename if known
|
|
73
87
|
|
|
74
|
-
:param
|
|
88
|
+
:param remove_annotations: Configures the removal of type annotations. True removes all annotations, False removes none. RemoveAnnotationsOptions can be used to configure the removal of specific annotations.
|
|
89
|
+
:type remove_annotations: bool or RemoveAnnotationsOptions
|
|
75
90
|
:param bool remove_pass: If Pass statements should be removed where possible
|
|
76
91
|
:param bool remove_literal_statements: If statements consisting of a single literal should be removed, including docstrings
|
|
77
92
|
:param bool combine_imports: Combine adjacent import statements where possible
|
|
@@ -84,6 +99,12 @@ def minify(
|
|
|
84
99
|
:type preserve_globals: list[str]
|
|
85
100
|
:param bool remove_object_base: If object as a base class may be removed
|
|
86
101
|
:param bool convert_posargs_to_args: If positional-only arguments will be converted to normal arguments
|
|
102
|
+
:param bool preserve_shebang: Keep any shebang interpreter directive from the source in the minified output
|
|
103
|
+
:param bool remove_asserts: If assert statements should be removed
|
|
104
|
+
:param bool remove_debug: If conditional statements that test '__debug__ is True' should be removed
|
|
105
|
+
:param bool remove_explicit_return_none: If explicit return None statements should be replaced with a bare return
|
|
106
|
+
:param bool remove_builtin_exception_brackets: If brackets should be removed when raising exceptions with no arguments
|
|
107
|
+
:param bool constant_folding: If literal expressions should be evaluated
|
|
87
108
|
|
|
88
109
|
:rtype: str
|
|
89
110
|
|
|
@@ -102,8 +123,20 @@ def minify(
|
|
|
102
123
|
if combine_imports:
|
|
103
124
|
module = CombineImports()(module)
|
|
104
125
|
|
|
105
|
-
if remove_annotations:
|
|
106
|
-
|
|
126
|
+
if isinstance(remove_annotations, bool):
|
|
127
|
+
remove_annotations_options = RemoveAnnotationsOptions(
|
|
128
|
+
remove_variable_annotations=remove_annotations,
|
|
129
|
+
remove_return_annotations=remove_annotations,
|
|
130
|
+
remove_argument_annotations=remove_annotations,
|
|
131
|
+
remove_class_attribute_annotations=remove_annotations,
|
|
132
|
+
)
|
|
133
|
+
elif isinstance(remove_annotations, RemoveAnnotationsOptions):
|
|
134
|
+
remove_annotations_options = remove_annotations
|
|
135
|
+
else:
|
|
136
|
+
raise TypeError('remove_annotations must be a bool or RemoveAnnotationsOptions')
|
|
137
|
+
|
|
138
|
+
if remove_annotations_options:
|
|
139
|
+
module = RemoveAnnotations(remove_annotations_options)(module)
|
|
107
140
|
|
|
108
141
|
if remove_pass:
|
|
109
142
|
module = RemovePass()(module)
|
|
@@ -111,13 +144,40 @@ def minify(
|
|
|
111
144
|
if remove_object_base:
|
|
112
145
|
module = RemoveObject()(module)
|
|
113
146
|
|
|
147
|
+
if remove_asserts:
|
|
148
|
+
module = RemoveAsserts()(module)
|
|
149
|
+
|
|
150
|
+
if remove_debug:
|
|
151
|
+
module = RemoveDebug()(module)
|
|
152
|
+
|
|
153
|
+
if remove_explicit_return_none:
|
|
154
|
+
module = RemoveExplicitReturnNone()(module)
|
|
155
|
+
|
|
156
|
+
if constant_folding:
|
|
157
|
+
module = FoldConstants()(module)
|
|
158
|
+
|
|
114
159
|
bind_names(module)
|
|
115
160
|
resolve_names(module)
|
|
116
161
|
|
|
162
|
+
if remove_builtin_exception_brackets and not module.tainted:
|
|
163
|
+
remove_no_arg_exception_call(module)
|
|
164
|
+
|
|
117
165
|
if module.tainted:
|
|
118
166
|
rename_globals = False
|
|
119
167
|
rename_locals = False
|
|
120
168
|
|
|
169
|
+
if preserve_locals is None:
|
|
170
|
+
preserve_locals = []
|
|
171
|
+
elif isinstance(preserve_locals, str):
|
|
172
|
+
preserve_locals = [preserve_locals]
|
|
173
|
+
if preserve_globals is None:
|
|
174
|
+
preserve_globals = []
|
|
175
|
+
elif isinstance(preserve_globals, str):
|
|
176
|
+
preserve_globals = [preserve_globals]
|
|
177
|
+
|
|
178
|
+
preserve_locals.extend(module.preserved)
|
|
179
|
+
preserve_globals.extend(module.preserved)
|
|
180
|
+
|
|
121
181
|
allow_rename_locals(module, rename_locals, preserve_locals)
|
|
122
182
|
allow_rename_globals(module, rename_globals, preserve_globals)
|
|
123
183
|
|
|
@@ -129,8 +189,30 @@ def minify(
|
|
|
129
189
|
if convert_posargs_to_args:
|
|
130
190
|
module = remove_posargs(module)
|
|
131
191
|
|
|
132
|
-
|
|
192
|
+
minified = unparse(module)
|
|
193
|
+
|
|
194
|
+
if preserve_shebang is True:
|
|
195
|
+
shebang_line = _find_shebang(source)
|
|
196
|
+
if shebang_line is not None:
|
|
197
|
+
return shebang_line + '\n' + minified
|
|
198
|
+
|
|
199
|
+
return minified
|
|
200
|
+
|
|
201
|
+
def _find_shebang(source):
|
|
202
|
+
"""
|
|
203
|
+
Find a shebang line in source
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
if isinstance(source, bytes):
|
|
207
|
+
shebang = re.match(br'^#!.*', source)
|
|
208
|
+
if shebang:
|
|
209
|
+
return shebang.group().decode()
|
|
210
|
+
else:
|
|
211
|
+
shebang = re.match(r'^#!.*', source)
|
|
212
|
+
if shebang:
|
|
213
|
+
return shebang.group()
|
|
133
214
|
|
|
215
|
+
return None
|
|
134
216
|
|
|
135
217
|
def unparse(module):
|
|
136
218
|
"""
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
from typing import List, Text, AnyStr, Optional, Any, Union
|
|
3
|
+
|
|
4
|
+
from .transforms.remove_annotations_options import RemoveAnnotationsOptions as RemoveAnnotationsOptions
|
|
5
|
+
|
|
6
|
+
class UnstableMinification(RuntimeError):
|
|
7
|
+
def __init__(self, exception: Any, source: Any, minified: Any): ...
|
|
8
|
+
|
|
9
|
+
def minify(
|
|
10
|
+
source: AnyStr,
|
|
11
|
+
filename: Optional[str] = ...,
|
|
12
|
+
remove_annotations: Union[bool, RemoveAnnotationsOptions] = ...,
|
|
13
|
+
remove_pass: bool = ...,
|
|
14
|
+
remove_literal_statements: bool = ...,
|
|
15
|
+
combine_imports: bool = ...,
|
|
16
|
+
hoist_literals: bool = ...,
|
|
17
|
+
rename_locals: bool = ...,
|
|
18
|
+
preserve_locals: Optional[List[Text]] = ...,
|
|
19
|
+
rename_globals: bool = ...,
|
|
20
|
+
preserve_globals: Optional[List[Text]] = ...,
|
|
21
|
+
remove_object_base: bool = ...,
|
|
22
|
+
convert_posargs_to_args: bool = ...,
|
|
23
|
+
preserve_shebang: bool = ...,
|
|
24
|
+
remove_asserts: bool = ...,
|
|
25
|
+
remove_debug: bool = ...,
|
|
26
|
+
remove_explicit_return_none: bool = ...,
|
|
27
|
+
remove_builtin_exception_brackets: bool = ...,
|
|
28
|
+
constant_folding: bool = ...
|
|
29
|
+
) -> Text: ...
|
|
30
|
+
|
|
31
|
+
def unparse(module: ast.Module) -> Text: ...
|
|
32
|
+
|
|
33
|
+
def awslambda(
|
|
34
|
+
source: AnyStr,
|
|
35
|
+
filename: Optional[Text] = ...,
|
|
36
|
+
entrypoint: Optional[Text] = ...
|
|
37
|
+
) -> Text: ...
|
python_minifier/__main__.py
CHANGED
|
@@ -1,126 +1,330 @@
|
|
|
1
1
|
from __future__ import print_function
|
|
2
2
|
|
|
3
|
-
import sys
|
|
4
|
-
|
|
5
3
|
import argparse
|
|
6
|
-
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
7
6
|
|
|
8
7
|
from python_minifier import minify
|
|
8
|
+
from python_minifier.transforms.remove_annotations_options import RemoveAnnotationsOptions
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
if sys.version_info >= (3, 8):
|
|
11
|
+
from importlib import metadata
|
|
12
|
+
try:
|
|
13
|
+
version = metadata.version('python-minifier')
|
|
14
|
+
except metadata.PackageNotFoundError:
|
|
15
|
+
version = '0.0.0'
|
|
16
|
+
else:
|
|
17
|
+
from pkg_resources import get_distribution, DistributionNotFound
|
|
18
|
+
try:
|
|
19
|
+
version = get_distribution('python_minifier').version
|
|
20
|
+
except DistributionNotFound:
|
|
21
|
+
version = '0.0.0'
|
|
14
22
|
|
|
15
23
|
|
|
16
24
|
def main():
|
|
25
|
+
"""
|
|
26
|
+
examples:
|
|
27
|
+
# Minifying stdin to stdout
|
|
28
|
+
pyminify -
|
|
29
|
+
|
|
30
|
+
# Minifying a file to stdout
|
|
31
|
+
pyminify example.py
|
|
32
|
+
|
|
33
|
+
# Minifying a file and writing to a different file
|
|
34
|
+
pyminify example.py --output example.min.py
|
|
17
35
|
|
|
18
|
-
|
|
36
|
+
# Minifying a file in place
|
|
37
|
+
pyminify example.py --in-place
|
|
19
38
|
|
|
20
|
-
|
|
39
|
+
# Minifying all *.py files in a directory
|
|
40
|
+
pyminify src/ --in-place
|
|
41
|
+
|
|
42
|
+
# Minifying multiple paths in place
|
|
43
|
+
pyminify file1.py file2.py src/ --in-place
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
args = parse_args()
|
|
47
|
+
|
|
48
|
+
if len(args.path) == 1 and args.path[0] == '-':
|
|
49
|
+
# minify stdin
|
|
50
|
+
source = sys.stdin.buffer.read() if sys.version_info >= (3, 0) else sys.stdin.read()
|
|
51
|
+
minified = do_minify(source, 'stdin', args)
|
|
52
|
+
if args.output:
|
|
53
|
+
with open(args.output, 'w') as f:
|
|
54
|
+
f.write(minified)
|
|
55
|
+
else:
|
|
56
|
+
sys.stdout.write(minified)
|
|
57
|
+
|
|
58
|
+
else:
|
|
59
|
+
# minify source paths
|
|
60
|
+
for path in source_modules(args):
|
|
61
|
+
if args.output or args.in_place:
|
|
62
|
+
sys.stdout.write(path + '\n')
|
|
63
|
+
|
|
64
|
+
with open(path, 'rb') as f:
|
|
65
|
+
source = f.read()
|
|
66
|
+
|
|
67
|
+
minified = do_minify(source, path, args)
|
|
68
|
+
|
|
69
|
+
if args.in_place:
|
|
70
|
+
with open(path, 'w') as f:
|
|
71
|
+
f.write(minified)
|
|
72
|
+
elif args.output:
|
|
73
|
+
with open(args.output, 'w') as f:
|
|
74
|
+
f.write(minified)
|
|
75
|
+
else:
|
|
76
|
+
sys.stdout.write(minified)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def parse_args():
|
|
80
|
+
parser = argparse.ArgumentParser(prog='pyminify', description='Minify Python source code', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=main.__doc__)
|
|
21
81
|
|
|
22
82
|
parser.add_argument(
|
|
83
|
+
'path',
|
|
84
|
+
nargs='+',
|
|
85
|
+
type=str,
|
|
86
|
+
help='The source file or directory to minify. Use "-" to read from stdin. Directories are recursively searched for ".py" files to minify. May be used multiple times',
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
output_options = parser.add_mutually_exclusive_group()
|
|
90
|
+
output_options.add_argument(
|
|
91
|
+
'--output', '-o',
|
|
92
|
+
action='store',
|
|
93
|
+
help='Path to write minified output. Can only be used when the source is a single module. Outputs to stdout by default',
|
|
94
|
+
dest='output'
|
|
95
|
+
)
|
|
96
|
+
output_options.add_argument(
|
|
97
|
+
'--in-place', '-i',
|
|
98
|
+
action='store_true',
|
|
99
|
+
help='Overwrite existing files. Required when there is more than one source module',
|
|
100
|
+
dest='in_place'
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Minification arguments
|
|
104
|
+
minification_options = parser.add_argument_group('minification options', 'Options that affect how the source is minified')
|
|
105
|
+
minification_options.add_argument(
|
|
23
106
|
'--no-combine-imports',
|
|
24
107
|
action='store_false',
|
|
25
108
|
help='Disable combining adjacent import statements',
|
|
26
109
|
dest='combine_imports',
|
|
27
110
|
)
|
|
28
|
-
|
|
111
|
+
minification_options.add_argument(
|
|
29
112
|
'--no-remove-pass',
|
|
30
113
|
action='store_false',
|
|
31
114
|
default=True,
|
|
32
115
|
help='Disable removing Pass statements',
|
|
33
116
|
dest='remove_pass',
|
|
34
117
|
)
|
|
35
|
-
|
|
118
|
+
minification_options.add_argument(
|
|
36
119
|
'--remove-literal-statements',
|
|
37
120
|
action='store_true',
|
|
38
121
|
help='Enable removing statements that are just a literal (including docstrings)',
|
|
39
122
|
dest='remove_literal_statements',
|
|
40
123
|
)
|
|
41
|
-
|
|
42
|
-
'--no-remove-annotations',
|
|
43
|
-
action='store_false',
|
|
44
|
-
help='Disable removing function and variable annotations',
|
|
45
|
-
dest='remove_annotations',
|
|
46
|
-
)
|
|
47
|
-
parser.add_argument(
|
|
124
|
+
minification_options.add_argument(
|
|
48
125
|
'--no-hoist-literals',
|
|
49
126
|
action='store_false',
|
|
50
127
|
help='Disable replacing string and bytes literals with variables',
|
|
51
128
|
dest='hoist_literals',
|
|
52
129
|
)
|
|
53
|
-
|
|
54
|
-
'--no-rename-locals',
|
|
130
|
+
minification_options.add_argument(
|
|
131
|
+
'--no-rename-locals',
|
|
132
|
+
action='store_false',
|
|
133
|
+
help='Disable shortening of local names',
|
|
134
|
+
dest='rename_locals'
|
|
55
135
|
)
|
|
56
|
-
|
|
136
|
+
minification_options.add_argument(
|
|
57
137
|
'--preserve-locals',
|
|
58
138
|
type=str,
|
|
59
139
|
action='append',
|
|
60
140
|
help='Comma separated list of local names that will not be shortened',
|
|
61
141
|
dest='preserve_locals',
|
|
142
|
+
metavar='LOCAL_NAMES'
|
|
62
143
|
)
|
|
63
|
-
|
|
64
|
-
'--rename-globals',
|
|
144
|
+
minification_options.add_argument(
|
|
145
|
+
'--rename-globals',
|
|
146
|
+
action='store_true',
|
|
147
|
+
help='Enable shortening of global names',
|
|
148
|
+
dest='rename_globals'
|
|
65
149
|
)
|
|
66
|
-
|
|
150
|
+
minification_options.add_argument(
|
|
67
151
|
'--preserve-globals',
|
|
68
152
|
type=str,
|
|
69
153
|
action='append',
|
|
70
154
|
help='Comma separated list of global names that will not be shortened',
|
|
71
155
|
dest='preserve_globals',
|
|
156
|
+
metavar='GLOBAL_NAMES'
|
|
72
157
|
)
|
|
73
|
-
|
|
158
|
+
minification_options.add_argument(
|
|
74
159
|
'--no-remove-object-base',
|
|
75
160
|
action='store_false',
|
|
76
161
|
help='Disable removing object from base class list',
|
|
77
162
|
dest='remove_object_base',
|
|
78
163
|
)
|
|
79
|
-
|
|
164
|
+
minification_options.add_argument(
|
|
80
165
|
'--no-convert-posargs-to-args',
|
|
81
166
|
action='store_false',
|
|
82
167
|
help='Disable converting positional only arguments to normal arguments',
|
|
83
168
|
dest='convert_posargs_to_args',
|
|
84
169
|
)
|
|
170
|
+
minification_options.add_argument(
|
|
171
|
+
'--no-preserve-shebang',
|
|
172
|
+
action='store_false',
|
|
173
|
+
help='Preserve any shebang line from the source',
|
|
174
|
+
dest='preserve_shebang',
|
|
175
|
+
)
|
|
176
|
+
minification_options.add_argument(
|
|
177
|
+
'--remove-asserts',
|
|
178
|
+
action='store_true',
|
|
179
|
+
help='Remove assert statements',
|
|
180
|
+
dest='remove_asserts',
|
|
181
|
+
)
|
|
182
|
+
minification_options.add_argument(
|
|
183
|
+
'--remove-debug',
|
|
184
|
+
action='store_true',
|
|
185
|
+
help='Remove conditional statements that test __debug__ is True',
|
|
186
|
+
dest='remove_debug',
|
|
187
|
+
)
|
|
188
|
+
minification_options.add_argument(
|
|
189
|
+
'--no-remove-explicit-return-none',
|
|
190
|
+
action='store_false',
|
|
191
|
+
help='Replace explicit return None with a bare return',
|
|
192
|
+
dest='remove_explicit_return_none',
|
|
193
|
+
)
|
|
194
|
+
minification_options.add_argument(
|
|
195
|
+
'--no-remove-builtin-exception-brackets',
|
|
196
|
+
action='store_false',
|
|
197
|
+
help='Disable removing brackets when raising builtin exceptions with no arguments',
|
|
198
|
+
dest='remove_exception_brackets',
|
|
199
|
+
)
|
|
200
|
+
minification_options.add_argument(
|
|
201
|
+
'--no-constant-folding',
|
|
202
|
+
action='store_false',
|
|
203
|
+
help='Disable evaluating literal expressions',
|
|
204
|
+
dest='constant_folding',
|
|
205
|
+
)
|
|
85
206
|
|
|
86
|
-
parser.
|
|
207
|
+
annotation_options = parser.add_argument_group('remove annotations options', 'Options that affect how annotations are removed')
|
|
208
|
+
annotation_options.add_argument(
|
|
209
|
+
'--no-remove-annotations',
|
|
210
|
+
action='store_false',
|
|
211
|
+
help='Disable removing all annotations',
|
|
212
|
+
dest='remove_annotations',
|
|
213
|
+
)
|
|
214
|
+
annotation_options.add_argument(
|
|
215
|
+
'--no-remove-variable-annotations',
|
|
216
|
+
action='store_false',
|
|
217
|
+
help='Disable removing variable annotations',
|
|
218
|
+
dest='remove_variable_annotations',
|
|
219
|
+
)
|
|
220
|
+
annotation_options.add_argument(
|
|
221
|
+
'--no-remove-return-annotations',
|
|
222
|
+
action='store_false',
|
|
223
|
+
help='Disable removing function return annotations',
|
|
224
|
+
dest='remove_return_annotations',
|
|
225
|
+
)
|
|
226
|
+
annotation_options.add_argument(
|
|
227
|
+
'--no-remove-argument-annotations',
|
|
228
|
+
action='store_false',
|
|
229
|
+
help='Disable removing function argument annotations',
|
|
230
|
+
dest='remove_argument_annotations',
|
|
231
|
+
)
|
|
232
|
+
annotation_options.add_argument(
|
|
233
|
+
'--remove-class-attribute-annotations',
|
|
234
|
+
action='store_true',
|
|
235
|
+
help='Enable removing class attribute annotations',
|
|
236
|
+
dest='remove_class_attribute_annotations',
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
parser.add_argument('--version', '-v', action='version', version=version)
|
|
87
240
|
|
|
88
241
|
args = parser.parse_args()
|
|
89
242
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
243
|
+
# Handle some invalid argument combinations
|
|
244
|
+
if '-' in args.path and len(args.path) != 1:
|
|
245
|
+
sys.stderr.write('error: multiple path arguments, reading from stdin not allowed\n')
|
|
246
|
+
sys.exit(1)
|
|
247
|
+
if '-' in args.path and args.in_place:
|
|
248
|
+
sys.stderr.write('error: reading from stdin, --in-place is not valid\n')
|
|
249
|
+
sys.exit(1)
|
|
250
|
+
if len(args.path) > 1 and not args.in_place:
|
|
251
|
+
sys.stderr.write('error: multiple path arguments, --in-place required\n')
|
|
252
|
+
sys.exit(1)
|
|
253
|
+
if len(args.path) == 1 and os.path.isdir(args.path[0]) and not args.in_place:
|
|
254
|
+
sys.stderr.write('error: path ' + args.path[0] + ' is a directory, --in-place required\n')
|
|
255
|
+
sys.exit(1)
|
|
256
|
+
|
|
257
|
+
if args.remove_class_attribute_annotations and not args.remove_annotations:
|
|
258
|
+
sys.stderr.write('error: --remove-class-attribute-annotations would do nothing when used with --no-remove-annotations\n')
|
|
259
|
+
sys.exit(1)
|
|
260
|
+
|
|
261
|
+
return args
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def source_modules(args):
|
|
265
|
+
|
|
266
|
+
def error(os_error):
|
|
267
|
+
raise os_error
|
|
268
|
+
|
|
269
|
+
for path_arg in args.path:
|
|
270
|
+
if os.path.isdir(path_arg):
|
|
271
|
+
for root, dirs, files in os.walk(path_arg, onerror=error, followlinks=True):
|
|
272
|
+
for file in files:
|
|
273
|
+
if file.endswith('.py') or file.endswith('.pyw'):
|
|
274
|
+
yield os.path.join(root, file)
|
|
275
|
+
else:
|
|
276
|
+
yield path_arg
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def do_minify(source, filename, minification_args):
|
|
95
280
|
|
|
96
281
|
preserve_globals = []
|
|
97
|
-
if
|
|
98
|
-
for arg in
|
|
282
|
+
if minification_args.preserve_globals:
|
|
283
|
+
for arg in minification_args.preserve_globals:
|
|
99
284
|
names = [name.strip() for name in arg.split(',') if name]
|
|
100
285
|
preserve_globals.extend(names)
|
|
101
286
|
|
|
102
287
|
preserve_locals = []
|
|
103
|
-
if
|
|
104
|
-
for arg in
|
|
288
|
+
if minification_args.preserve_locals:
|
|
289
|
+
for arg in minification_args.preserve_locals:
|
|
105
290
|
names = [name.strip() for name in arg.split(',') if name]
|
|
106
291
|
preserve_locals.extend(names)
|
|
107
292
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
remove_object_base=args.remove_object_base,
|
|
122
|
-
convert_posargs_to_args=args.convert_posargs_to_args,
|
|
293
|
+
if minification_args.remove_annotations is False:
|
|
294
|
+
remove_annotations = RemoveAnnotationsOptions(
|
|
295
|
+
remove_variable_annotations=False,
|
|
296
|
+
remove_return_annotations=False,
|
|
297
|
+
remove_argument_annotations=False,
|
|
298
|
+
remove_class_attribute_annotations=False,
|
|
299
|
+
)
|
|
300
|
+
else:
|
|
301
|
+
remove_annotations = RemoveAnnotationsOptions(
|
|
302
|
+
remove_variable_annotations=minification_args.remove_variable_annotations,
|
|
303
|
+
remove_return_annotations=minification_args.remove_return_annotations,
|
|
304
|
+
remove_argument_annotations=minification_args.remove_argument_annotations,
|
|
305
|
+
remove_class_attribute_annotations=minification_args.remove_class_attribute_annotations,
|
|
123
306
|
)
|
|
307
|
+
|
|
308
|
+
return minify(
|
|
309
|
+
source,
|
|
310
|
+
filename=filename,
|
|
311
|
+
combine_imports=minification_args.combine_imports,
|
|
312
|
+
remove_pass=minification_args.remove_pass,
|
|
313
|
+
remove_annotations=remove_annotations,
|
|
314
|
+
remove_literal_statements=minification_args.remove_literal_statements,
|
|
315
|
+
hoist_literals=minification_args.hoist_literals,
|
|
316
|
+
rename_locals=minification_args.rename_locals,
|
|
317
|
+
preserve_locals=preserve_locals,
|
|
318
|
+
rename_globals=minification_args.rename_globals,
|
|
319
|
+
preserve_globals=preserve_globals,
|
|
320
|
+
remove_object_base=minification_args.remove_object_base,
|
|
321
|
+
convert_posargs_to_args=minification_args.convert_posargs_to_args,
|
|
322
|
+
preserve_shebang=minification_args.preserve_shebang,
|
|
323
|
+
remove_asserts=minification_args.remove_asserts,
|
|
324
|
+
remove_debug=minification_args.remove_debug,
|
|
325
|
+
remove_explicit_return_none=minification_args.remove_explicit_return_none,
|
|
326
|
+
remove_builtin_exception_brackets=minification_args.remove_exception_brackets,
|
|
327
|
+
constant_folding=minification_args.constant_folding
|
|
124
328
|
)
|
|
125
329
|
|
|
126
330
|
|
python_minifier/ast_compare.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import ast
|
|
1
|
+
import python_minifier.ast_compat as ast
|
|
2
|
+
|
|
3
|
+
from python_minifier.util import is_ast_node
|
|
2
4
|
|
|
3
5
|
|
|
4
6
|
class CompareError(RuntimeError):
|
|
@@ -16,9 +18,7 @@ class CompareError(RuntimeError):
|
|
|
16
18
|
|
|
17
19
|
def namespace(self, node):
|
|
18
20
|
if hasattr(node, 'namespace'):
|
|
19
|
-
if
|
|
20
|
-
hasattr(ast, 'AsyncFunctionDef') and isinstance(node.namespace, ast.AsyncFunctionDef)
|
|
21
|
-
):
|
|
21
|
+
if is_ast_node(node.namespace, (ast.FunctionDef, ast.ClassDef, 'AsyncFunctionDef')):
|
|
22
22
|
return self.namespace(node.namespace) + '.' + node.namespace.name
|
|
23
23
|
elif isinstance(node.namespace, ast.Module):
|
|
24
24
|
return ''
|
|
@@ -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)
|