Skip to content

Commit a7760a8

Browse files
committed
Remove trailing return
1 parent 1f28e90 commit a7760a8

File tree

6 files changed

+197
-32
lines changed

6 files changed

+197
-32
lines changed
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
def important(a):
2-
if not a:
2+
if a > 3:
3+
return a
4+
if a < 2:
35
return None
46
a.adjust(1)
5-
return a
7+
return None

docs/source/transforms/remove_explicit_return_none.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
Remove Explicit Return None
22
===========================
33

4-
This transforms and ``return None`` statement into a ``return`` statement.
4+
This transforms any ``return None`` statement into a ``return`` statement.
55
A return statement with no value is equivalent to ``return None``.
66

7+
Removes any ``return None`` or ``return`` statements that are the last statement in a function.
8+
79
The transform is always safe to use and enabled by default. Disable by passing the ``remove_explicit_return_none=False`` argument to the :func:`python_minifier.minify` function,
810
or passing ``--no-remove-explicit-remove-none`` to the pyminify command.
911

src/python_minifier/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from python_minifier.transforms.remove_annotations import RemoveAnnotations
2424
from python_minifier.transforms.remove_asserts import RemoveAsserts
2525
from python_minifier.transforms.remove_debug import RemoveDebug
26-
from python_minifier.transforms.implicit_return_none import implicit_return_none
26+
from python_minifier.transforms.remove_explicit_return_none import RemoveExplicitReturnNone
2727
from python_minifier.transforms.remove_literal_statements import RemoveLiteralStatements
2828
from python_minifier.transforms.remove_object_base import RemoveObject
2929
from python_minifier.transforms.remove_pass import RemovePass
@@ -131,7 +131,7 @@ def minify(
131131
module = RemoveDebug()(module)
132132

133133
if remove_explicit_return_none:
134-
module = implicit_return_none(module)
134+
module = RemoveExplicitReturnNone()(module)
135135

136136
bind_names(module)
137137
resolve_names(module)

src/python_minifier/transforms/implicit_return_none.py

Lines changed: 0 additions & 21 deletions
This file was deleted.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import ast
2+
import sys
3+
4+
from python_minifier.transforms.suite_transformer import SuiteTransformer
5+
from python_minifier.util import is_ast_node
6+
7+
class RemoveExplicitReturnNone(SuiteTransformer):
8+
def __call__(self, node):
9+
return self.visit(node)
10+
11+
def visit_Return(self, node):
12+
assert isinstance(node, ast.Return)
13+
14+
# Remove an explicit `return None`
15+
16+
if sys.version_info < (3, 4) and is_ast_node(node.value, 'Name') and node.value.id == 'None':
17+
# explicit return None
18+
node.value = None
19+
20+
elif sys.version_info >= (3, 4) and is_ast_node(node.value, 'NameConstant') and node.value.value is None:
21+
# explicit return None
22+
node.value = None
23+
24+
return node
25+
26+
def visit_FunctionDef(self, node):
27+
assert is_ast_node(node, (ast.FunctionDef, 'AsyncFunctionDef'))
28+
29+
node.body = [self.visit(node) for node in node.body]
30+
31+
# Remove an explicit `return` from the end of a function
32+
33+
if len(node.body) > 0 and isinstance(node.body[-1], ast.Return) and node.body[-1].value is None:
34+
# explicit return None
35+
node.body.pop()
36+
37+
if len(node.body) == 0:
38+
node.body = [self.add_child(ast.Expr(value=ast.Num(0)), parent=node)]
39+
40+
return node
41+
42+
def visit_AsyncFunctionDef(self, node):
43+
return self.visit_FunctionDef(node)
Lines changed: 145 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,176 @@
11
import ast
22
from python_minifier import unparse
33
from python_minifier.ast_compare import compare_ast
4-
from python_minifier.transforms.implicit_return_none import implicit_return_none
4+
from python_minifier.transforms.remove_explicit_return_none import RemoveExplicitReturnNone
55

66

77
def remove_return_none(source):
88
module = ast.parse(source, 'remove_return_none')
99

10-
return implicit_return_none(module)
10+
return RemoveExplicitReturnNone()(module)
1111

12-
def test_remove_return_none():
12+
def test_trailing_remove_return_none():
13+
source = 'def a():a=4;return None'
14+
expected = 'def a():a=4'
15+
16+
expected_ast = ast.parse(expected)
17+
actual_ast = remove_return_none(source)
18+
compare_ast(expected_ast, actual_ast)
19+
20+
assert unparse(actual_ast) == expected
21+
22+
def test_trailing_implicit_return_none():
23+
source = 'def a():a=4;return'
24+
expected = 'def a():a=4'
25+
expected_ast = ast.parse(expected)
26+
actual_ast = remove_return_none(source)
27+
compare_ast(expected_ast, actual_ast)
28+
assert unparse(actual_ast) == expected
29+
30+
31+
def test_trailing_remove_return_none_empty_suite():
1332
source = 'def a():return None'
14-
expected = 'def a():return'
33+
expected = 'def a():0'
1534

1635
expected_ast = ast.parse(expected)
1736
actual_ast = remove_return_none(source)
1837
compare_ast(expected_ast, actual_ast)
1938

2039
assert unparse(actual_ast) == expected
2140

22-
def test_implicit_return_none():
41+
def test_trailing_implicit_return_none_empty_suite():
2342
source = 'def a():return'
43+
expected = 'def a():0'
44+
expected_ast = ast.parse(expected)
45+
actual_ast = remove_return_none(source)
46+
compare_ast(expected_ast, actual_ast)
47+
assert unparse(actual_ast) == expected
48+
49+
def test_trailing_return_value_unchanged():
50+
source = 'def a():return 0'
2451
expected = source
2552
expected_ast = ast.parse(expected)
2653
actual_ast = remove_return_none(source)
2754
compare_ast(expected_ast, actual_ast)
2855
assert unparse(actual_ast) == expected
2956

57+
def test_remove_return_none():
58+
source = '''
59+
def a():
60+
if a: return None
61+
return None
62+
'''
63+
expected = 'def a():\n\tif a:return'
64+
65+
expected_ast = ast.parse(expected)
66+
actual_ast = remove_return_none(source)
67+
compare_ast(expected_ast, actual_ast)
68+
69+
assert unparse(actual_ast) == expected
70+
71+
def test_implicit_return_none():
72+
source = '''
73+
def a():
74+
if a: return
75+
return
76+
'''
77+
expected = 'def a():\n\tif a:return'
78+
expected_ast = ast.parse(expected)
79+
actual_ast = remove_return_none(source)
80+
compare_ast(expected_ast, actual_ast)
81+
assert unparse(actual_ast) == expected
82+
3083
def test_return_value_unchanged():
31-
source = 'def a():return 0'
84+
source = '''
85+
def a():
86+
if a: return 1
87+
return 3
88+
'''
89+
expected = 'def a():\n\tif a:return 1\n\treturn 3'
90+
expected_ast = ast.parse(expected)
91+
actual_ast = remove_return_none(source)
92+
compare_ast(expected_ast, actual_ast)
93+
assert unparse(actual_ast) == expected
94+
95+
def test_async_trailing_remove_return_none():
96+
source = 'async def a():a=4;return None'
97+
expected = 'async def a():a=4'
98+
99+
expected_ast = ast.parse(expected)
100+
actual_ast = remove_return_none(source)
101+
compare_ast(expected_ast, actual_ast)
102+
103+
assert unparse(actual_ast) == expected
104+
105+
def test_async_trailing_implicit_return_none():
106+
source = 'async def a():a=4;return'
107+
expected = 'async def a():a=4'
108+
expected_ast = ast.parse(expected)
109+
actual_ast = remove_return_none(source)
110+
compare_ast(expected_ast, actual_ast)
111+
assert unparse(actual_ast) == expected
112+
113+
114+
def test_async_trailing_remove_return_none_empty_suite():
115+
source = 'async def a():return None'
116+
expected = 'async def a():0'
117+
118+
expected_ast = ast.parse(expected)
119+
actual_ast = remove_return_none(source)
120+
compare_ast(expected_ast, actual_ast)
121+
122+
assert unparse(actual_ast) == expected
123+
124+
def test_async_trailing_implicit_return_none_empty_suite():
125+
source = 'async def a():return'
126+
expected = 'async def a():0'
127+
expected_ast = ast.parse(expected)
128+
actual_ast = remove_return_none(source)
129+
compare_ast(expected_ast, actual_ast)
130+
assert unparse(actual_ast) == expected
131+
132+
def test_async_trailing_return_value_unchanged():
133+
source = 'async def a():return 0'
32134
expected = source
33135
expected_ast = ast.parse(expected)
34136
actual_ast = remove_return_none(source)
35137
compare_ast(expected_ast, actual_ast)
36138
assert unparse(actual_ast) == expected
37139

140+
def test_async_remove_return_none():
141+
source = '''
142+
async def a():
143+
if a: return None
144+
return None
145+
'''
146+
expected = 'async def a():\n\tif a:return'
147+
148+
expected_ast = ast.parse(expected)
149+
actual_ast = remove_return_none(source)
150+
compare_ast(expected_ast, actual_ast)
151+
152+
assert unparse(actual_ast) == expected
153+
154+
def test_async_implicit_return_none():
155+
source = '''
156+
async def a():
157+
if a: return
158+
return
159+
'''
160+
expected = 'async def a():\n\tif a:return'
161+
expected_ast = ast.parse(expected)
162+
actual_ast = remove_return_none(source)
163+
compare_ast(expected_ast, actual_ast)
164+
assert unparse(actual_ast) == expected
165+
166+
def test_async_return_value_unchanged():
167+
source = '''
168+
async def a():
169+
if a: return 1
170+
return 3
171+
'''
172+
expected = 'async def a():\n\tif a:return 1\n\treturn 3'
173+
expected_ast = ast.parse(expected)
174+
actual_ast = remove_return_none(source)
175+
compare_ast(expected_ast, actual_ast)
176+
assert unparse(actual_ast) == expected

0 commit comments

Comments
 (0)