Skip to content

Commit 85b03e2

Browse files
committed
Fix for empty JoinedStr Str parts in 3.6 & 3.7
1 parent 77927a8 commit 85b03e2

File tree

6 files changed

+93
-7
lines changed

6 files changed

+93
-7
lines changed

src/python_minifier/expression_printer.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ def __init__(self):
1414
self.unicode_literals = False
1515

1616
self.precedences = {
17-
'Lambda': 2,
18-
'IfExp': 3,
17+
'Lambda': 2, # Lamdda
18+
'IfExp': 3, # IfExp
1919
'comprehension': 3.5,
20-
'Or': 4,
20+
'Or': 4, # BoolOp
2121
'And': 5,
2222
'Not': 6,
23-
'In': 7, 'NotIn': 7, 'Is': 7, 'IsNot': 7, 'Lt': 7, 'LtE': 7, 'Gt': 7, 'GtE': 7, 'NotEq': 7, 'Eq': 7,
23+
'In': 7, 'NotIn': 7, 'Is': 7, 'IsNot': 7, 'Lt': 7, 'LtE': 7, 'Gt': 7, 'GtE': 7, 'NotEq': 7, 'Eq': 7, # Compare
2424
'BitOr': 8,
2525
'BitXor': 9,
2626
'BitAnd': 10,
@@ -190,11 +190,18 @@ def visit_Dict(self, node):
190190

191191
if k is None:
192192
self.code += '**'
193+
194+
if 0 < self.precedence(v) <=7:
195+
self.code += '('
196+
self._expression(v)
197+
self.code += ')'
198+
else:
199+
self._expression(v)
193200
else:
194201
self._expression(k)
195202
self.code += ':'
196203

197-
self._expression(v)
204+
self._expression(v)
198205

199206
self.code += '}'
200207

src/python_minifier/f_string.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,11 @@ def __str__(self):
104104
return min(candidates, key=len)
105105

106106
def str_for(self, s, quote):
107-
return str(MiniString(s, quote)).replace('{', '{{').replace('}', '}}')
107+
mini_s = str(MiniString(s, quote)).replace('{', '{{').replace('}', '}}')
108+
109+
if mini_s == '':
110+
return '\\\n'
111+
return mini_s
108112

109113

110114
class FormattedValue(ExpressionPrinter):

src/python_minifier/module_printer.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import ast
2+
import sys
23

34
from .expression_printer import ExpressionPrinter
45

@@ -160,7 +161,14 @@ def visit_Return(self, node):
160161

161162
self.token_break()
162163
self.code += 'return'
163-
if node.value is not None:
164+
if isinstance(node.value, ast.Tuple):
165+
if sys.version_info < (3, 8) and hasattr(ast, 'Starred') and [n for n in node.value.elts if isinstance(n, ast.Starred)]:
166+
self.code += '('
167+
self._testlist(node.value)
168+
self.code += ')'
169+
else:
170+
self._testlist(node.value)
171+
elif node.value is not None:
164172
self._testlist(node.value)
165173
self.end_statement()
166174

test/test_dict_expansion.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import ast
2+
import sys
3+
import pytest
4+
from python_minifier import unparse
5+
from python_minifier.ast_compare import compare_ast
6+
7+
def test_dict_expanson():
8+
if sys.version_info < (3, 5):
9+
pytest.skip('dict expansion not allowed in python < 3.5')
10+
11+
source = [
12+
r'{**a>>9}',
13+
r'{**(a or b)}',
14+
r'{**(a and b)}',
15+
r'{**a+b}',
16+
r'{**(lambda a:a)}',
17+
r'{**(a<b)}',
18+
r'{**(yield a())}',
19+
r'{**(a if a else a)}'
20+
]
21+
22+
for expression in source:
23+
expected_ast = ast.parse(expression)
24+
minified = unparse(expected_ast)
25+
compare_ast(expected_ast, ast.parse(minified))
26+
assert expression == minified

test/test_empty_fstring.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import ast
2+
import sys
3+
import pytest
4+
from python_minifier import unparse
5+
from python_minifier.ast_compare import compare_ast
6+
7+
def test_fstring_empty_str():
8+
if sys.version_info < (3, 6):
9+
pytest.skip('f-string expressions not allowed in python < 3.6')
10+
11+
source = r'''
12+
f"""\
13+
{fg_br}"""
14+
'''
15+
16+
print(source)
17+
expected_ast = ast.parse(source)
18+
actual_ast = unparse(expected_ast)
19+
compare_ast(expected_ast, ast.parse(actual_ast))

test/test_iterable_unpacking.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import ast
2+
import sys
3+
import pytest
4+
from python_minifier import unparse
5+
from python_minifier.ast_compare import compare_ast
6+
7+
def test_return():
8+
if sys.version_info < (3, 0):
9+
pytest.skip('Iterable unpacking in return not allowed in python < 3.0')
10+
11+
elif sys.version_info < (3, 8):
12+
# Parenthesis are required
13+
source = 'def a():return(True,*[False])'
14+
15+
else:
16+
# Parenthesis not required
17+
source = 'def a():return True,*[False]'
18+
19+
expected_ast = ast.parse(source)
20+
minified = unparse(expected_ast)
21+
compare_ast(expected_ast, ast.parse(minified))
22+
assert source == minified

0 commit comments

Comments
 (0)