forked from dflook/python-minifier
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path__init__.py
More file actions
187 lines (138 loc) · 5.85 KB
/
__init__.py
File metadata and controls
187 lines (138 loc) · 5.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
"""
This package transforms python source code strings or ast.Module Nodes into
a 'minified' representation of the same source code.
"""
import ast
from python_minifier.ast_compare import CompareError, compare_ast
from python_minifier.module_printer import ModulePrinter
from python_minifier.rename import (
rename_literals,
bind_names,
resolve_names,
rename,
allow_rename_globals,
allow_rename_locals,
add_namespace,
)
from python_minifier.transforms.combine_imports import CombineImports
from python_minifier.transforms.remove_annotations import RemoveAnnotations
from python_minifier.transforms.remove_literal_statements import RemoveLiteralStatements
from python_minifier.transforms.remove_object_base import RemoveObject
from python_minifier.transforms.remove_pass import RemovePass
from python_minifier.transforms.remove_posargs import remove_posargs
class UnstableMinification(RuntimeError):
"""
Raised when a minified module differs from the original module in an unexpected way.
This is raised when the minifier generates source code that doesn't parse back into the
original module (after known transformations).
This should never occur and is a bug.
"""
def __init__(self, exception, source, minified):
self.exception = exception
self.source = source
self.minified = minified
def __str__(self):
return 'Minification was unstable! Please create an issue at https://github.com/dflook/python-minifier/issues'
def minify(
source,
filename=None,
remove_annotations=True,
remove_pass=True,
remove_literal_statements=False,
combine_imports=True,
hoist_literals=True,
rename_locals=True,
preserve_locals=None,
rename_globals=False,
preserve_globals=None,
remove_object_base=True,
convert_posargs_to_args=True,
):
"""
Minify a python module
The module is transformed according the the arguments.
If all transformation arguments are False, no transformations are made to the AST, the returned string will
parse into exactly the same module.
Using the default arguments only transformations that are always or almost always safe are enabled.
:param str source: The python module source code
:param str filename: The original source filename if known
:param bool remove_annotations: If type annotations should be removed where possible
:param bool remove_pass: If Pass statements should be removed where possible
:param bool remove_literal_statements: If statements consisting of a single literal should be removed, including docstrings
:param bool combine_imports: Combine adjacent import statements where possible
:param bool hoist_literals: If str and byte literals may be hoisted to the module level where possible.
:param bool rename_locals: If local names may be shortened
:param preserve_locals: Locals names to leave unchanged when rename_locals is True
:type preserve_locals: list[str]
:param bool rename_globals: If global names may be shortened
:param preserve_globals: Global names to leave unchanged when rename_globals is True
:type preserve_globals: list[str]
:param bool remove_object_base: If object as a base class may be removed
:param bool convert_posargs_to_args: If positional-only arguments will be converted to normal arguments
:rtype: str
"""
filename = filename or 'python_minifier.minify source'
# This will raise if the source file can't be parsed
module = ast.parse(source, filename)
add_namespace(module)
if remove_literal_statements:
module = RemoveLiteralStatements()(module)
if combine_imports:
module = CombineImports()(module)
if remove_annotations:
module = RemoveAnnotations()(module)
if remove_pass:
module = RemovePass()(module)
if remove_object_base:
module = RemoveObject()(module)
bind_names(module)
resolve_names(module)
if module.tainted:
rename_globals = False
rename_locals = False
allow_rename_locals(module, rename_locals, preserve_locals)
allow_rename_globals(module, rename_globals, preserve_globals)
if hoist_literals:
rename_literals(module)
rename(module, prefix_globals=not rename_globals, preserved_globals=preserve_globals)
if convert_posargs_to_args:
module = remove_posargs(module)
return unparse(module)
def unparse(module):
"""
Turn a module AST into python code
This returns an exact representation of the given module,
such that it can be parsed back into the same AST.
:param module: The module to turn into python code
:type: module: :class:`ast.Module`
:rtype: str
"""
assert isinstance(module, ast.Module)
printer = ModulePrinter()
printer(module)
try:
minified_module = ast.parse(printer.code, 'python_minifier.unparse output')
except SyntaxError as syntax_error:
raise UnstableMinification(syntax_error, '', printer.code)
try:
compare_ast(module, minified_module)
except CompareError as compare_error:
raise UnstableMinification(compare_error, '', printer.code)
return printer.code
def awslambda(source, filename=None, entrypoint=None):
"""
Minify a python module for use as an AWS Lambda function
This returns a string suitable for embedding in a cloudformation template.
When minifying, all transformations are enabled.
:param str source: The python module source code
:param str filename: The original source filename if known
:param entrypoint: The lambda entrypoint function
:type entrypoint: str or NoneType
:rtype: str
"""
rename_globals = True
if entrypoint is None:
rename_globals = False
return minify(
source, filename, remove_literal_statements=True, rename_globals=rename_globals, preserve_globals=[entrypoint],
)