Skip to content

Commit 5b9ae1b

Browse files
committed
Add simple constant folding
1 parent 22513c3 commit 5b9ae1b

2 files changed

Lines changed: 62 additions & 1 deletion

File tree

src/python_minifier/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
)
2121

2222
from python_minifier.transforms.combine_imports import CombineImports
23+
from python_minifier.transforms.constant_folding import FoldConstants
2324
from python_minifier.transforms.remove_annotations import RemoveAnnotations
2425
from python_minifier.transforms.remove_annotations_options import RemoveAnnotationsOptions
2526
from python_minifier.transforms.remove_asserts import RemoveAsserts
@@ -69,7 +70,8 @@ def minify(
6970
remove_asserts=False,
7071
remove_debug=False,
7172
remove_explicit_return_none=True,
72-
remove_builtin_exception_brackets=True
73+
remove_builtin_exception_brackets=True,
74+
constant_folding=True
7375
):
7476
"""
7577
Minify a python module
@@ -102,6 +104,7 @@ def minify(
102104
:param bool remove_debug: If conditional statements that test '__debug__ is True' should be removed
103105
:param bool remove_explicit_return_none: If explicit return None statements should be replaced with a bare return
104106
:param bool remove_builtin_exception_brackets: If brackets should be removed when raising exceptions with no arguments
107+
:param bool constant_folding: If constant expressions should be evaluated
105108
106109
:rtype: str
107110
@@ -150,6 +153,9 @@ def minify(
150153
if remove_explicit_return_none:
151154
module = RemoveExplicitReturnNone()(module)
152155

156+
if constant_folding:
157+
module = FoldConstants()(module)
158+
153159
bind_names(module)
154160
resolve_names(module)
155161

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import ast
2+
3+
from python_minifier.expression_printer import ExpressionPrinter
4+
from python_minifier.transforms.suite_transformer import SuiteTransformer
5+
from python_minifier.util import is_ast_node
6+
7+
8+
class FoldConstants(SuiteTransformer):
9+
"""
10+
Fold Constants if it would reduce the size of the source
11+
"""
12+
13+
def __init__(self):
14+
super(FoldConstants, self).__init__()
15+
16+
def visit_BinOp(self, node):
17+
18+
node.left = self.visit(node.left)
19+
node.right = self.visit(node.right)
20+
21+
# Check this is a constant expression that could be folded
22+
if not is_ast_node(node.left, (ast.Num, ast.Str, ast.Bytes, ast.NameConstant)):
23+
return node
24+
if not is_ast_node(node.right, (ast.Num, ast.Str, ast.Bytes, ast.NameConstant)):
25+
return node
26+
27+
expression_printer = ExpressionPrinter()
28+
29+
try:
30+
original_expression = expression_printer(node)
31+
value = eval(original_expression)
32+
except Exception as e:
33+
return node
34+
35+
if isinstance(value, str):
36+
new_node = ast.Str(s=value)
37+
elif isinstance(value, bytes):
38+
new_node = ast.Bytes(s=value)
39+
elif isinstance(value, bool):
40+
new_node = ast.NameConstant(value=value)
41+
elif isinstance(value, (int, float, complex)):
42+
new_node = ast.Num(n=value)
43+
else:
44+
return node
45+
46+
expression_printer = ExpressionPrinter()
47+
folded_expression = expression_printer(new_node)
48+
49+
if len(folded_expression) > len(original_expression):
50+
# Result is longer than original expression
51+
return node
52+
53+
assert eval(folded_expression) == value
54+
55+
return self.add_child(new_node, node.parent, node.namespace)

0 commit comments

Comments
 (0)