python-minifier 2.8.1__tar.gz → 2.10.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. {python_minifier-2.8.1 → python_minifier-2.10.0}/PKG-INFO +5 -4
  2. {python_minifier-2.8.1 → python_minifier-2.10.0}/README.md +2 -2
  3. {python_minifier-2.8.1 → python_minifier-2.10.0}/setup.py +4 -4
  4. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/__init__.py +50 -6
  5. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/__init__.pyi +7 -3
  6. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/__main__.py +93 -17
  7. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/ast_compare.py +1 -1
  8. python_minifier-2.10.0/src/python_minifier/ast_compat.py +40 -0
  9. python_minifier-2.10.0/src/python_minifier/ast_printer.py +130 -0
  10. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/expression_printer.py +214 -270
  11. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/f_string.py +89 -33
  12. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/module_printer.py +276 -284
  13. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/rename/bind_names.py +24 -6
  14. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/rename/binding.py +48 -3
  15. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/rename/mapper.py +16 -1
  16. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/rename/rename_literals.py +17 -2
  17. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/rename/renamer.py +1 -1
  18. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/rename/resolve_names.py +28 -10
  19. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/rename/util.py +10 -1
  20. python_minifier-2.10.0/src/python_minifier/token_printer.py +299 -0
  21. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/transforms/combine_imports.py +8 -5
  22. python_minifier-2.10.0/src/python_minifier/transforms/constant_folding.py +114 -0
  23. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/transforms/remove_annotations.py +25 -5
  24. python_minifier-2.10.0/src/python_minifier/transforms/remove_annotations_options.py +35 -0
  25. python_minifier-2.10.0/src/python_minifier/transforms/remove_annotations_options.pyi +17 -0
  26. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/transforms/remove_asserts.py +1 -1
  27. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/transforms/remove_debug.py +1 -1
  28. python_minifier-2.10.0/src/python_minifier/transforms/remove_exception_brackets.py +124 -0
  29. python_minifier-2.10.0/src/python_minifier/transforms/remove_explicit_return_none.py +38 -0
  30. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/transforms/remove_literal_statements.py +1 -1
  31. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/transforms/remove_object_base.py +4 -1
  32. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/transforms/remove_pass.py +1 -1
  33. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/transforms/remove_posargs.py +1 -1
  34. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/transforms/suite_transformer.py +4 -1
  35. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/util.py +7 -7
  36. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier.egg-info/PKG-INFO +6 -5
  37. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier.egg-info/SOURCES.txt +18 -1
  38. python_minifier-2.10.0/test/test_bind_names.py +1354 -0
  39. python_minifier-2.10.0/test/test_bind_names_python2.py +605 -0
  40. python_minifier-2.10.0/test/test_bind_names_python33.py +620 -0
  41. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_combine_imports.py +80 -0
  42. python_minifier-2.10.0/test/test_folding.py +136 -0
  43. python_minifier-2.10.0/test/test_fstring.py +103 -0
  44. python_minifier-2.10.0/test/test_generic_types.py +278 -0
  45. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_hoist_literals.py +26 -0
  46. python_minifier-2.10.0/test/test_import.py +30 -0
  47. python_minifier-2.10.0/test/test_is_ast_node.py +40 -0
  48. python_minifier-2.10.0/test/test_remove_annotations.py +325 -0
  49. python_minifier-2.10.0/test/test_remove_exception_brackets.py +165 -0
  50. python_minifier-2.10.0/test/test_remove_explicit_return_none.py +218 -0
  51. python_minifier-2.8.1/test/test_empty_fstring.py +0 -19
  52. python_minifier-2.8.1/test/test_remove_annotations.py +0 -218
  53. {python_minifier-2.8.1 → python_minifier-2.10.0}/LICENSE +0 -0
  54. {python_minifier-2.8.1 → python_minifier-2.10.0}/setup.cfg +0 -0
  55. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/ministring.py +0 -0
  56. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/py.typed +0 -0
  57. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/rename/__init__.py +0 -0
  58. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/rename/name_generator.py +0 -0
  59. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier/transforms/__init__.py +0 -0
  60. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier.egg-info/dependency_links.txt +0 -0
  61. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier.egg-info/entry_points.txt +0 -0
  62. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier.egg-info/top_level.txt +0 -0
  63. {python_minifier-2.8.1 → python_minifier-2.10.0}/src/python_minifier.egg-info/zip-safe +0 -0
  64. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_assignment_expressions.py +0 -0
  65. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_await_fstring.py +0 -0
  66. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_comprehension_rename.py +0 -0
  67. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_decorator_expressions.py +0 -0
  68. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_dict_expansion.py +0 -0
  69. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_iterable_unpacking.py +0 -0
  70. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_match.py +0 -0
  71. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_match_rename.py +0 -0
  72. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_name_generator.py +0 -0
  73. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_nonlocal.py +0 -0
  74. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_posargs.py +0 -0
  75. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_preserve_shebang.py +0 -0
  76. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_remove_assert.py +0 -0
  77. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_remove_debug.py +0 -0
  78. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_remove_literal_statements.py +0 -0
  79. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_remove_object.py +0 -0
  80. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_remove_pass.py +0 -0
  81. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_rename_builtins.py +0 -0
  82. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_rename_locals.py +0 -0
  83. {python_minifier-2.8.1 → python_minifier-2.10.0}/test/test_slice.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python_minifier
3
- Version: 2.8.1
3
+ Version: 2.10.0
4
4
  Summary: Transform Python source code into it's most compact representation
5
5
  Home-page: https://github.com/dflook/python-minifier
6
6
  Author: Daniel Flook
@@ -22,13 +22,14 @@ Classifier: Programming Language :: Python :: 3.8
22
22
  Classifier: Programming Language :: Python :: 3.9
23
23
  Classifier: Programming Language :: Python :: 3.10
24
24
  Classifier: Programming Language :: Python :: 3.11
25
+ Classifier: Programming Language :: Python :: 3.12
25
26
  Classifier: Programming Language :: Python :: 2
26
27
  Classifier: Programming Language :: Python :: 2.7
27
28
  Classifier: Programming Language :: Python :: Implementation :: CPython
28
29
  Classifier: Programming Language :: Python :: Implementation :: PyPy
29
30
  Classifier: Intended Audience :: Developers
30
31
  Classifier: Topic :: Software Development
31
- Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, <3.12
32
+ Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, <3.13
32
33
  Description-Content-Type: text/markdown
33
34
  License-File: LICENSE
34
35
 
@@ -38,7 +39,7 @@ Transforms Python source code into its most compact representation.
38
39
 
39
40
  [Try it out!](https://python-minifier.com)
40
41
 
41
- python-minifier currently supports Python 2.7 and Python 3.3 to 3.11. Previous releases supported Python 2.6.
42
+ python-minifier currently supports Python 2.7 and Python 3.3 to 3.12. Previous releases supported Python 2.6.
42
43
 
43
44
  * [PyPI](https://pypi.org/project/python-minifier/)
44
45
  * [Documentation](https://dflook.github.io/python-minifier/)
@@ -141,7 +142,7 @@ $ pip install python-minifier
141
142
  Note that python-minifier depends on the python interpreter for parsing source code,
142
143
  so install using a version of python appropriate for your source.
143
144
 
144
- python-minifier runs with and can minify code written for Python 2.7 and Python 3.3 to 3.11.
145
+ python-minifier runs with and can minify code written for Python 2.7 and Python 3.3 to 3.12.
145
146
 
146
147
  ## Usage
147
148
 
@@ -4,7 +4,7 @@ Transforms Python source code into its most compact representation.
4
4
 
5
5
  [Try it out!](https://python-minifier.com)
6
6
 
7
- python-minifier currently supports Python 2.7 and Python 3.3 to 3.11. Previous releases supported Python 2.6.
7
+ python-minifier currently supports Python 2.7 and Python 3.3 to 3.12. Previous releases supported Python 2.6.
8
8
 
9
9
  * [PyPI](https://pypi.org/project/python-minifier/)
10
10
  * [Documentation](https://dflook.github.io/python-minifier/)
@@ -107,7 +107,7 @@ $ pip install python-minifier
107
107
  Note that python-minifier depends on the python interpreter for parsing source code,
108
108
  so install using a version of python appropriate for your source.
109
109
 
110
- python-minifier runs with and can minify code written for Python 2.7 and Python 3.3 to 3.11.
110
+ python-minifier runs with and can minify code written for Python 2.7 and Python 3.3 to 3.12.
111
111
 
112
112
  ## Usage
113
113
 
@@ -1,5 +1,4 @@
1
1
  import os.path
2
- import sys
3
2
 
4
3
  from setuptools import setup, find_packages
5
4
 
@@ -23,12 +22,12 @@ setup(
23
22
 
24
23
  package_dir={'': 'src'},
25
24
  packages=find_packages('src'),
26
- package_data={"python_minifier": ["py.typed", "__init__.pyi"]},
25
+ package_data={"python_minifier": ["py.typed", "*.pyi", "rename/*.pyi", "transforms/*.pyi"]},
27
26
  long_description=long_desc,
28
27
  long_description_content_type='text/markdown',
29
28
 
30
- python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, <3.12',
31
- version='2.8.1',
29
+ python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, <3.13',
30
+ version='2.10.0',
32
31
 
33
32
  classifiers=[
34
33
  'Development Status :: 5 - Production/Stable',
@@ -44,6 +43,7 @@ setup(
44
43
  'Programming Language :: Python :: 3.9',
45
44
  'Programming Language :: Python :: 3.10',
46
45
  'Programming Language :: Python :: 3.11',
46
+ 'Programming Language :: Python :: 3.12',
47
47
  'Programming Language :: Python :: 2',
48
48
  'Programming Language :: Python :: 2.7',
49
49
  'Programming Language :: Python :: Implementation :: CPython',
@@ -4,7 +4,7 @@ a 'minified' representation of the same source code.
4
4
 
5
5
  """
6
6
 
7
- import ast
7
+ import python_minifier.ast_compat as ast
8
8
  import re
9
9
 
10
10
  from python_minifier.ast_compare import CompareError, compare_ast
@@ -20,9 +20,13 @@ from python_minifier.rename import (
20
20
  )
21
21
 
22
22
  from python_minifier.transforms.combine_imports import CombineImports
23
+ from python_minifier.transforms.constant_folding import FoldConstants
23
24
  from python_minifier.transforms.remove_annotations import RemoveAnnotations
25
+ from python_minifier.transforms.remove_annotations_options import RemoveAnnotationsOptions
24
26
  from python_minifier.transforms.remove_asserts import RemoveAsserts
25
27
  from python_minifier.transforms.remove_debug import RemoveDebug
28
+ from python_minifier.transforms.remove_explicit_return_none import RemoveExplicitReturnNone
29
+ from python_minifier.transforms.remove_exception_brackets import remove_no_arg_exception_call
26
30
  from python_minifier.transforms.remove_literal_statements import RemoveLiteralStatements
27
31
  from python_minifier.transforms.remove_object_base import RemoveObject
28
32
  from python_minifier.transforms.remove_pass import RemovePass
@@ -51,7 +55,7 @@ class UnstableMinification(RuntimeError):
51
55
  def minify(
52
56
  source,
53
57
  filename=None,
54
- remove_annotations=True,
58
+ remove_annotations=RemoveAnnotationsOptions(),
55
59
  remove_pass=True,
56
60
  remove_literal_statements=False,
57
61
  combine_imports=True,
@@ -64,7 +68,10 @@ def minify(
64
68
  convert_posargs_to_args=True,
65
69
  preserve_shebang=True,
66
70
  remove_asserts=False,
67
- remove_debug=False
71
+ remove_debug=False,
72
+ remove_explicit_return_none=True,
73
+ remove_builtin_exception_brackets=True,
74
+ constant_folding=True
68
75
  ):
69
76
  """
70
77
  Minify a python module
@@ -78,7 +85,8 @@ def minify(
78
85
  :param str source: The python module source code
79
86
  :param str filename: The original source filename if known
80
87
 
81
- :param bool remove_annotations: If type annotations should be removed where possible
88
+ :param remove_annotations: Configures the removal of type annotations. True removes all annotations, False removes none. RemoveAnnotationsOptions can be used to configure the removal of specific annotations.
89
+ :type remove_annotations: bool or RemoveAnnotationsOptions
82
90
  :param bool remove_pass: If Pass statements should be removed where possible
83
91
  :param bool remove_literal_statements: If statements consisting of a single literal should be removed, including docstrings
84
92
  :param bool combine_imports: Combine adjacent import statements where possible
@@ -94,6 +102,9 @@ def minify(
94
102
  :param bool preserve_shebang: Keep any shebang interpreter directive from the source in the minified output
95
103
  :param bool remove_asserts: If assert statements should be removed
96
104
  :param bool remove_debug: If conditional statements that test '__debug__ is True' should be removed
105
+ :param bool remove_explicit_return_none: If explicit return None statements should be replaced with a bare return
106
+ :param bool remove_builtin_exception_brackets: If brackets should be removed when raising exceptions with no arguments
107
+ :param bool constant_folding: If literal expressions should be evaluated
97
108
 
98
109
  :rtype: str
99
110
 
@@ -112,8 +123,20 @@ def minify(
112
123
  if combine_imports:
113
124
  module = CombineImports()(module)
114
125
 
115
- if remove_annotations:
116
- module = RemoveAnnotations()(module)
126
+ if isinstance(remove_annotations, bool):
127
+ remove_annotations_options = RemoveAnnotationsOptions(
128
+ remove_variable_annotations=remove_annotations,
129
+ remove_return_annotations=remove_annotations,
130
+ remove_argument_annotations=remove_annotations,
131
+ remove_class_attribute_annotations=remove_annotations,
132
+ )
133
+ elif isinstance(remove_annotations, RemoveAnnotationsOptions):
134
+ remove_annotations_options = remove_annotations
135
+ else:
136
+ raise TypeError('remove_annotations must be a bool or RemoveAnnotationsOptions')
137
+
138
+ if remove_annotations_options:
139
+ module = RemoveAnnotations(remove_annotations_options)(module)
117
140
 
118
141
  if remove_pass:
119
142
  module = RemovePass()(module)
@@ -127,13 +150,34 @@ def minify(
127
150
  if remove_debug:
128
151
  module = RemoveDebug()(module)
129
152
 
153
+ if remove_explicit_return_none:
154
+ module = RemoveExplicitReturnNone()(module)
155
+
156
+ if constant_folding:
157
+ module = FoldConstants()(module)
158
+
130
159
  bind_names(module)
131
160
  resolve_names(module)
132
161
 
162
+ if remove_builtin_exception_brackets and not module.tainted:
163
+ remove_no_arg_exception_call(module)
164
+
133
165
  if module.tainted:
134
166
  rename_globals = False
135
167
  rename_locals = False
136
168
 
169
+ if preserve_locals is None:
170
+ preserve_locals = []
171
+ elif isinstance(preserve_locals, str):
172
+ preserve_locals = [preserve_locals]
173
+ if preserve_globals is None:
174
+ preserve_globals = []
175
+ elif isinstance(preserve_globals, str):
176
+ preserve_globals = [preserve_globals]
177
+
178
+ preserve_locals.extend(module.preserved)
179
+ preserve_globals.extend(module.preserved)
180
+
137
181
  allow_rename_locals(module, rename_locals, preserve_locals)
138
182
  allow_rename_globals(module, rename_globals, preserve_globals)
139
183
 
@@ -1,6 +1,7 @@
1
1
  import ast
2
- from typing import List, Text, AnyStr, Optional, Any
2
+ from typing import List, Text, AnyStr, Optional, Any, Union
3
3
 
4
+ from .transforms.remove_annotations_options import RemoveAnnotationsOptions as RemoveAnnotationsOptions
4
5
 
5
6
  class UnstableMinification(RuntimeError):
6
7
  def __init__(self, exception: Any, source: Any, minified: Any): ...
@@ -8,7 +9,7 @@ class UnstableMinification(RuntimeError):
8
9
  def minify(
9
10
  source: AnyStr,
10
11
  filename: Optional[str] = ...,
11
- remove_annotations: bool = ...,
12
+ remove_annotations: Union[bool, RemoveAnnotationsOptions] = ...,
12
13
  remove_pass: bool = ...,
13
14
  remove_literal_statements: bool = ...,
14
15
  combine_imports: bool = ...,
@@ -21,7 +22,10 @@ def minify(
21
22
  convert_posargs_to_args: bool = ...,
22
23
  preserve_shebang: bool = ...,
23
24
  remove_asserts: bool = ...,
24
- remove_debug: bool = ...
25
+ remove_debug: bool = ...,
26
+ remove_explicit_return_none: bool = ...,
27
+ remove_builtin_exception_brackets: bool = ...,
28
+ constant_folding: bool = ...
25
29
  ) -> Text: ...
26
30
 
27
31
  def unparse(module: ast.Module) -> Text: ...
@@ -1,17 +1,24 @@
1
1
  from __future__ import print_function
2
2
 
3
- import sys
4
- import os
5
-
6
3
  import argparse
7
- from pkg_resources import get_distribution, DistributionNotFound
4
+ import os
5
+ import sys
8
6
 
9
7
  from python_minifier import minify
10
-
11
- try:
12
- version = get_distribution('python_minifier').version
13
- except DistributionNotFound:
14
- version = '0.0.0'
8
+ from python_minifier.transforms.remove_annotations_options import RemoveAnnotationsOptions
9
+
10
+ if sys.version_info >= (3, 8):
11
+ from importlib import metadata
12
+ try:
13
+ version = metadata.version('python-minifier')
14
+ except metadata.PackageNotFoundError:
15
+ version = '0.0.0'
16
+ else:
17
+ from pkg_resources import get_distribution, DistributionNotFound
18
+ try:
19
+ version = get_distribution('python_minifier').version
20
+ except DistributionNotFound:
21
+ version = '0.0.0'
15
22
 
16
23
 
17
24
  def main():
@@ -68,6 +75,7 @@ examples:
68
75
  else:
69
76
  sys.stdout.write(minified)
70
77
 
78
+
71
79
  def parse_args():
72
80
  parser = argparse.ArgumentParser(prog='pyminify', description='Minify Python source code', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=main.__doc__)
73
81
 
@@ -113,12 +121,6 @@ def parse_args():
113
121
  help='Enable removing statements that are just a literal (including docstrings)',
114
122
  dest='remove_literal_statements',
115
123
  )
116
- minification_options.add_argument(
117
- '--no-remove-annotations',
118
- action='store_false',
119
- help='Disable removing function and variable annotations',
120
- dest='remove_annotations',
121
- )
122
124
  minification_options.add_argument(
123
125
  '--no-hoist-literals',
124
126
  action='store_false',
@@ -183,6 +185,56 @@ def parse_args():
183
185
  help='Remove conditional statements that test __debug__ is True',
184
186
  dest='remove_debug',
185
187
  )
188
+ minification_options.add_argument(
189
+ '--no-remove-explicit-return-none',
190
+ action='store_false',
191
+ help='Replace explicit return None with a bare return',
192
+ dest='remove_explicit_return_none',
193
+ )
194
+ minification_options.add_argument(
195
+ '--no-remove-builtin-exception-brackets',
196
+ action='store_false',
197
+ help='Disable removing brackets when raising builtin exceptions with no arguments',
198
+ dest='remove_exception_brackets',
199
+ )
200
+ minification_options.add_argument(
201
+ '--no-constant-folding',
202
+ action='store_false',
203
+ help='Disable evaluating literal expressions',
204
+ dest='constant_folding',
205
+ )
206
+
207
+ annotation_options = parser.add_argument_group('remove annotations options', 'Options that affect how annotations are removed')
208
+ annotation_options.add_argument(
209
+ '--no-remove-annotations',
210
+ action='store_false',
211
+ help='Disable removing all annotations',
212
+ dest='remove_annotations',
213
+ )
214
+ annotation_options.add_argument(
215
+ '--no-remove-variable-annotations',
216
+ action='store_false',
217
+ help='Disable removing variable annotations',
218
+ dest='remove_variable_annotations',
219
+ )
220
+ annotation_options.add_argument(
221
+ '--no-remove-return-annotations',
222
+ action='store_false',
223
+ help='Disable removing function return annotations',
224
+ dest='remove_return_annotations',
225
+ )
226
+ annotation_options.add_argument(
227
+ '--no-remove-argument-annotations',
228
+ action='store_false',
229
+ help='Disable removing function argument annotations',
230
+ dest='remove_argument_annotations',
231
+ )
232
+ annotation_options.add_argument(
233
+ '--remove-class-attribute-annotations',
234
+ action='store_true',
235
+ help='Enable removing class attribute annotations',
236
+ dest='remove_class_attribute_annotations',
237
+ )
186
238
 
187
239
  parser.add_argument('--version', '-v', action='version', version=version)
188
240
 
@@ -202,8 +254,13 @@ def parse_args():
202
254
  sys.stderr.write('error: path ' + args.path[0] + ' is a directory, --in-place required\n')
203
255
  sys.exit(1)
204
256
 
257
+ if args.remove_class_attribute_annotations and not args.remove_annotations:
258
+ sys.stderr.write('error: --remove-class-attribute-annotations would do nothing when used with --no-remove-annotations\n')
259
+ sys.exit(1)
260
+
205
261
  return args
206
262
 
263
+
207
264
  def source_modules(args):
208
265
 
209
266
  def error(os_error):
@@ -218,6 +275,7 @@ def source_modules(args):
218
275
  else:
219
276
  yield path_arg
220
277
 
278
+
221
279
  def do_minify(source, filename, minification_args):
222
280
 
223
281
  preserve_globals = []
@@ -232,12 +290,27 @@ def do_minify(source, filename, minification_args):
232
290
  names = [name.strip() for name in arg.split(',') if name]
233
291
  preserve_locals.extend(names)
234
292
 
293
+ if minification_args.remove_annotations is False:
294
+ remove_annotations = RemoveAnnotationsOptions(
295
+ remove_variable_annotations=False,
296
+ remove_return_annotations=False,
297
+ remove_argument_annotations=False,
298
+ remove_class_attribute_annotations=False,
299
+ )
300
+ else:
301
+ remove_annotations = RemoveAnnotationsOptions(
302
+ remove_variable_annotations=minification_args.remove_variable_annotations,
303
+ remove_return_annotations=minification_args.remove_return_annotations,
304
+ remove_argument_annotations=minification_args.remove_argument_annotations,
305
+ remove_class_attribute_annotations=minification_args.remove_class_attribute_annotations,
306
+ )
307
+
235
308
  return minify(
236
309
  source,
237
310
  filename=filename,
238
311
  combine_imports=minification_args.combine_imports,
239
312
  remove_pass=minification_args.remove_pass,
240
- remove_annotations=minification_args.remove_annotations,
313
+ remove_annotations=remove_annotations,
241
314
  remove_literal_statements=minification_args.remove_literal_statements,
242
315
  hoist_literals=minification_args.hoist_literals,
243
316
  rename_locals=minification_args.rename_locals,
@@ -248,7 +321,10 @@ def do_minify(source, filename, minification_args):
248
321
  convert_posargs_to_args=minification_args.convert_posargs_to_args,
249
322
  preserve_shebang=minification_args.preserve_shebang,
250
323
  remove_asserts=minification_args.remove_asserts,
251
- remove_debug=minification_args.remove_debug
324
+ remove_debug=minification_args.remove_debug,
325
+ remove_explicit_return_none=minification_args.remove_explicit_return_none,
326
+ remove_builtin_exception_brackets=minification_args.remove_exception_brackets,
327
+ constant_folding=minification_args.constant_folding
252
328
  )
253
329
 
254
330
 
@@ -1,4 +1,4 @@
1
- import ast
1
+ import python_minifier.ast_compat as ast
2
2
 
3
3
  from python_minifier.util import is_ast_node
4
4
 
@@ -0,0 +1,40 @@
1
+ """
2
+ The is a backwards compatible shim for the ast module.
3
+
4
+ This is the best way to make the ast module work the same in both python 2 and 3.
5
+ This is essentially what the ast module was doing until 3.12, when it started throwing
6
+ deprecation warnings.
7
+ """
8
+
9
+ from ast import *
10
+
11
+ # Ideally we don't import anything else
12
+
13
+ if 'TypeAlias' in globals():
14
+
15
+ # Add n and s properties to Constant so it can stand in for Num, Str and Bytes
16
+ Constant.n = property(lambda self: self.value, lambda self, value: setattr(self, 'value', value)) # type: ignore[assignment]
17
+ Constant.s = property(lambda self: self.value, lambda self, value: setattr(self, 'value', value)) # type: ignore[assignment]
18
+
19
+ # These classes are redefined from the ones in ast that complain about deprecation
20
+ # They will continue to work once they are removed from ast
21
+
22
+ class Str(Constant): # type: ignore[no-redef]
23
+ def __new__(cls, s, *args, **kwargs):
24
+ return Constant(value=s, *args, **kwargs)
25
+
26
+ class Bytes(Constant): # type: ignore[no-redef]
27
+ def __new__(cls, s, *args, **kwargs):
28
+ return Constant(value=s, *args, **kwargs)
29
+
30
+ class Num(Constant): # type: ignore[no-redef]
31
+ def __new__(cls, n, *args, **kwargs):
32
+ return Constant(value=n, *args, **kwargs)
33
+
34
+ class NameConstant(Constant): # type: ignore[no-redef]
35
+ def __new__(cls, *args, **kwargs):
36
+ return Constant(*args, **kwargs)
37
+
38
+ class Ellipsis(Constant): # type: ignore[no-redef]
39
+ def __new__(cls, *args, **kwargs):
40
+ return Constant(value=literal_eval('...'), *args, **kwargs)
@@ -0,0 +1,130 @@
1
+ """
2
+ Print a representation of an AST
3
+
4
+ This prints a human readable representation of the nodes in the AST.
5
+ The goal is to make it easy to see what the AST looks like, and to
6
+ make it easy to compare two ASTs.
7
+
8
+ This is not intended to be a complete representation of the AST, some
9
+ fields or field names may be omitted for clarity. It should still be precise and unambiguous.
10
+
11
+ """
12
+
13
+ import python_minifier.ast_compat as ast
14
+
15
+ from python_minifier.util import is_ast_node
16
+
17
+ INDENT = ' '
18
+
19
+ # The field name that can be omitted for each node
20
+ # Either it's the only field or would otherwise be obvious
21
+ default_fields = {
22
+ 'Constant': 'value',
23
+ 'Num': 'n',
24
+ 'Str': 's',
25
+ 'Bytes': 's',
26
+ 'NameConstant': 'value',
27
+ 'FormattedValue': 'value',
28
+ 'JoinedStr': 'values',
29
+ 'List': 'elts',
30
+ 'Tuple': 'elts',
31
+ 'Set': 'elts',
32
+ 'Name': 'id',
33
+ 'Expr': 'value',
34
+ 'UnaryOp': 'op',
35
+ 'BinOp': 'op',
36
+ 'BoolOp': 'op',
37
+ 'Call': 'func',
38
+ 'Index': 'value',
39
+ 'ExtSlice': 'dims',
40
+ 'Assert': 'test',
41
+ 'Delete': 'targets',
42
+ 'Import': 'names',
43
+ 'If': 'test',
44
+ 'While': 'test',
45
+ 'Try': 'handlers',
46
+ 'TryExcept': 'handlers',
47
+ 'With': 'items',
48
+ 'withitem': 'context_expr',
49
+ 'FunctionDef': 'name',
50
+ 'arg': 'arg',
51
+ 'Return': 'value',
52
+ 'Yield': 'value',
53
+ 'YieldFrom': 'value',
54
+ 'Global': 'names',
55
+ 'Nonlocal': 'names',
56
+ 'ClassDef': 'name',
57
+ 'AsyncFunctionDef': 'name',
58
+ 'Await': 'value',
59
+ 'AsyncWith': 'items',
60
+ 'Raise': 'exc',
61
+ 'Subscript': 'value',
62
+ 'Attribute': 'value',
63
+ 'AugAssign': 'op',
64
+ }
65
+
66
+ def is_literal(node, field):
67
+ if hasattr(ast, 'Constant') and isinstance(node, ast.Constant) and field == 'value':
68
+ return True
69
+
70
+ if is_ast_node(node, ast.Num) and field == 'n':
71
+ return True
72
+
73
+ if is_ast_node(node, ast.Str) and field == 's':
74
+ return True
75
+
76
+ if is_ast_node(node, 'Bytes') and field == 's':
77
+ return True
78
+
79
+ if is_ast_node(node, 'NameConstant') and field == 'value':
80
+ return True
81
+
82
+ return False
83
+
84
+ def print_ast(node):
85
+ if not isinstance(node, ast.AST):
86
+ return repr(node)
87
+
88
+ s = ''
89
+
90
+ node_name = node.__class__.__name__
91
+ s += node_name
92
+ s += '('
93
+
94
+ first = True
95
+ for field, value in ast.iter_fields(node):
96
+ if not value and not is_literal(node, field):
97
+ # Don't bother printing fields that are empty, except for literals
98
+ continue
99
+
100
+ if field == 'ctx':
101
+ # Don't print the ctx, it's always apparent from context
102
+ continue
103
+
104
+ if first:
105
+ first = False
106
+ else:
107
+ s += ', '
108
+
109
+ if default_fields.get(node_name) != field:
110
+ s += field + '='
111
+
112
+ if isinstance(value, ast.AST):
113
+ s += print_ast(value)
114
+ elif isinstance(value, list):
115
+ s += '['
116
+ first_list = True
117
+ for item in value:
118
+ if first_list:
119
+ first_list = False
120
+ else:
121
+ s += ','
122
+
123
+ for line in print_ast(item).splitlines():
124
+ s += '\n' + INDENT + line
125
+ s += '\n]'
126
+ else:
127
+ s += repr(value)
128
+
129
+ s += ')'
130
+ return s