Skip to content

Commit 8ee36d2

Browse files
committed
Ad convert posargs to args transform
1 parent e869134 commit 8ee36d2

File tree

8 files changed

+114
-3
lines changed

8 files changed

+114
-3
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,15 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7-
## [2.2.2] - 2019-11-16
7+
## [2.3.0] - Unreleased
8+
9+
### Added
10+
- Optional source transform:
11+
- convert positional arguments to normal arguments, enabled by default
812

913
### Fixed
1014
- Unnecessary spaces after ',' in tuple values
15+
- Removing annotations for positional-only arguments (Thanks [luk3yx](https://github.com/luk3yx)!)
1116

1217
## [2.2.1] - 2019-11-03
1318

@@ -76,6 +81,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7681
- pyminify command
7782

7883

84+
[2.3.0]: https://github.com/dflook/python-minifier/compare/2.2.1...2.3.0
7985
[2.2.1]: https://github.com/dflook/python-minifier/compare/2.2.0...2.2.1
8086
[2.2.0]: https://github.com/dflook/python-minifier/compare/2.1.2...2.2.0
8187
[2.1.2]: https://github.com/dflook/python-minifier/compare/2.1.1...2.1.2
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
def name(p1, p2, /, p_or_kw, *, kw): pass
2+
def name(p1, p2=None, /, p_or_kw=None, *, kw): pass
3+
def name(p1, p2=None, /, *, kw): pass
4+
def name(p1, p2=None, /): pass
5+
def name(p1, p2, /, p_or_kw): pass
6+
def name(p1, p2, /): pass
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Combine Positional Argument to Arguments
2+
========================================
3+
4+
This transform converts positional only arguments into normal arguments by removing the '/' separator in the
5+
argument list.
6+
7+
This transform is almost always safe to use and enabled by default.
8+
9+
Disable this source transformation by passing the ``convert_posargs_to_args=False`` argument to the :func:`python_minifier.minify` function,
10+
or passing ``--no-convert-posargs-to-args`` to the pyminify command.
11+
12+
Example
13+
-------
14+
15+
Input
16+
~~~~~
17+
18+
.. literalinclude:: combine_imports.py
19+
20+
Output
21+
~~~~~~
22+
23+
.. literalinclude:: combine_imports.min.py
24+
:language: python

docs/source/transforms/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ They can be enabled or disabled through the minify function, or passing options
1616
remove_annotations
1717
rename_locals
1818
remove_object_base
19+
convert_posargs_to_args
1920

2021
.. toctree::
2122
:caption: Disabled by default

src/python_minifier/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from python_minifier.transforms.remove_literal_statements import RemoveLiteralStatements
2323
from python_minifier.transforms.remove_object_base import RemoveObject
2424
from python_minifier.transforms.remove_pass import RemovePass
25+
from python_minifier.transforms.remove_posargs import remove_posargs
2526

2627

2728
class UnstableMinification(RuntimeError):
@@ -55,7 +56,8 @@ def minify(
5556
preserve_locals=None,
5657
rename_globals=False,
5758
preserve_globals=None,
58-
remove_object_base=True
59+
remove_object_base=True,
60+
convert_posargs_to_args=True
5961
):
6062
"""
6163
Minify a python module
@@ -123,6 +125,9 @@ def minify(
123125

124126
rename(module, prefix_globals=not rename_globals, preserved_globals=preserve_globals)
125127

128+
if convert_posargs_to_args:
129+
module = remove_posargs(module)
130+
126131
return unparse(module)
127132

128133

src/python_minifier/__main__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515

1616
def main():
1717

18-
parser = argparse.ArgumentParser(description='Minify Python source')
18+
parser = argparse.ArgumentParser(prog='pyminify',
19+
description='Minify Python source')
1920

2021
parser.add_argument(
2122
'path',
@@ -86,6 +87,12 @@ def main():
8687
help='Disable removing object from base class list',
8788
dest='remove_object_base',
8889
)
90+
parser.add_argument(
91+
'--no-convert-posargs-to-args',
92+
action='store_false',
93+
help='Disable converting positional only arguments to normal arguments',
94+
dest='convert_posargs_to_args',
95+
)
8996

9097
parser.add_argument('-v', '--version', action='version', version=version)
9198

@@ -123,6 +130,7 @@ def main():
123130
rename_globals=args.rename_globals,
124131
preserve_globals=preserve_globals,
125132
remove_object_base=args.remove_object_base,
133+
convert_posargs_to_args=args.convert_posargs_to_args
126134
)
127135
)
128136

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import ast
2+
3+
def remove_posargs(node):
4+
if isinstance(node, ast.arguments):
5+
if hasattr(node, 'posonlyargs'):
6+
node.args = node.posonlyargs + node.args
7+
node.posonlyargs = []
8+
9+
for child in ast.iter_child_nodes(node):
10+
remove_posargs(child)
11+
12+
return node

test/test_posargs.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import pytest
44
from python_minifier import unparse
55
from python_minifier.ast_compare import compare_ast
6+
from python_minifier.transforms.remove_posargs import remove_posargs
67

78
def test_pep():
89
if sys.version_info < (3, 8):
@@ -31,3 +32,51 @@ def combined_example(pos_only, /, standard, *, kwd_only):
3132
expected_ast = ast.parse(source)
3233
actual_ast = unparse(expected_ast)
3334
compare_ast(expected_ast, ast.parse(actual_ast))
35+
36+
def test_convert():
37+
if sys.version_info < (3, 8):
38+
pytest.skip('No Assignment expressions in python < 3.8')
39+
40+
source = '''
41+
def name(p1, p2, /, p_or_kw, *, kw): pass
42+
def name(p1, p2=None, /, p_or_kw=None, *, kw): pass
43+
def name(p1, p2=None, /, *, kw): pass
44+
def name(p1, p2=None, /): pass
45+
def name(p1, p2, /, p_or_kw): pass
46+
def name(p1, p2, /): pass
47+
def name(p_or_kw, *, kw): pass
48+
def name(*, kw): pass
49+
50+
def standard_arg(arg):
51+
print(arg)
52+
def pos_only_arg(arg, /):
53+
print(arg)
54+
def kwd_only_arg(*, arg):
55+
print(arg)
56+
def combined_example(pos_only, /, standard, *, kwd_only):
57+
print(pos_only, standard, kwd_only)
58+
'''
59+
60+
expected = '''
61+
def name(p1, p2, p_or_kw, *, kw): pass
62+
def name(p1, p2=None, p_or_kw=None, *, kw): pass
63+
def name(p1, p2=None, *, kw): pass
64+
def name(p1, p2=None): pass
65+
def name(p1, p2, p_or_kw): pass
66+
def name(p1, p2): pass
67+
def name(p_or_kw, *, kw): pass
68+
def name(*, kw): pass
69+
70+
def standard_arg(arg):
71+
print(arg)
72+
def pos_only_arg(arg):
73+
print(arg)
74+
def kwd_only_arg(*, arg):
75+
print(arg)
76+
def combined_example(pos_only, standard, *, kwd_only):
77+
print(pos_only, standard, kwd_only)
78+
'''
79+
80+
expected_ast = ast.parse(expected)
81+
actual_ast = unparse(remove_posargs(ast.parse(source)))
82+
compare_ast(expected_ast, ast.parse(actual_ast))

0 commit comments

Comments
 (0)