Skip to content

Commit cbf2407

Browse files
committed
Enhance python linting
1 parent 171954c commit cbf2407

26 files changed

+210
-182
lines changed

.config/ruff.toml

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,39 @@ builtins = ["unicode"]
66

77
line-length = 200
88

9+
exclude = ["docs"]
10+
911
[lint]
1012
# Enable most rules
1113
select = [
12-
"E", # pycodestyle errors
13-
"W", # pycodestyle warnings
14-
"F", # Pyflakes
15-
"I", # isort
14+
"E", # pycodestyle errors
15+
"W", # pycodestyle warnings
16+
"F", # Pyflakes
17+
"I", # isort
18+
"S", # flake8-bandit (security)
19+
"ERA", # eradicate (commented-out code)
20+
"YTT", # flake8-2020 (sys.version checks)
21+
"FBT", # flake8-boolean-trap (boolean positional arguments)
22+
"A", # flake8-builtins (shadowing builtins)
23+
"COM", # flake8-commas (trailing commas)
24+
"C4", # flake8-comprehensions (comprehension improvements)
25+
"ISC", # flake8-implicit-str-concat (string concatenation)
26+
"PYI", # flake8-pyi (stub file checks)
27+
"PT", # flake8-pytest-style (pytest best practices)
1628
]
1729

1830
ignore = [
1931
"I001", # Import block unsorted
20-
"W292", # No newline at end of file
21-
"W293", # Blank line contains whitespace
32+
"S101", # Use of assert - intentional for validation and type checking
33+
"S307", # Use of eval() - intentional and safe in this codebase (constant folding, validation)
34+
"FBT002", # Boolean default positional arguments - API design choice throughout codebase
35+
"COM812", # Missing trailing comma - we prohibit trailing commas instead
2236
]
2337

2438
# Per-file ignores for specific compatibility needs
2539
[lint.per-file-ignores]
26-
# AST compatibility module needs star imports for Python version compatibility
27-
"src/python_minifier/ast_compat.py" = ["F403", "F405"]
40+
# AST compatibility module needs star imports and shadows Ellipsis builtin for Python version compatibility
41+
"src/python_minifier/ast_compat.py" = ["F403", "F405", "A001"]
2842

2943
# __init__.py files intentionally re-export for public API
3044
"*/__init__.py" = ["F401"]
@@ -37,6 +51,29 @@ ignore = [
3751
# Compatibility imports in utility modules
3852
"src/python_minifier/rename/util.py" = ["F401"]
3953

54+
# Broad exception handling needed for candidate generation (try different quote styles)
55+
"src/python_minifier/f_string.py" = ["S112"]
56+
"src/python_minifier/t_string.py" = ["S112"]
57+
58+
# random.choice() used for variable name generation, not cryptography
59+
"src/python_minifier/rename/name_generator.py" = ["S311"]
60+
61+
# Test files need exec() to validate minified code behavior and subprocess calls for integration tests
62+
"test/**/*.py" = ["S102", "S603"]
63+
64+
# Testing AST constant nodes requires constructing them with boolean literal values
65+
"test/test_is_constant_node.py" = ["FBT003"]
66+
67+
# Hypothesis test generators intentionally use names matching AST concepts (Ellipsis, iter, slice) and use assume(False)
68+
# Comments like # "text{expr}" document string patterns, not commented-out code
69+
"hypo_test/**/*.py" = ["A001", "FBT003", "ERA001"]
70+
71+
# Typing tests intentionally use wrong types, including parameter names that look like passwords
72+
"typing_test/**/*.py" = ["S106"]
73+
74+
[lint.flake8-boolean-trap]
75+
extend-allowed-calls = ["type", "ast.NameConstant", "ast.Constant", "NameConstant", "Constant", "ast_compat.NameConstant", "assume"]
76+
4077
[lint.isort]
4178
force-single-line = false
4279
known-first-party = ["python_minifier"]

.github/workflows/test.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,6 @@ jobs:
125125
uses: astral-sh/ruff-action@57714a7c8a2e59f32539362ba31877a1957dded1 # v3.5.1
126126
with:
127127
args: --config=.config/ruff.toml check
128-
src: src
129128

130129
lint_dockerfiles:
131130
runs-on: ubuntu-24.04

corpus_test/generate_report.py

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
from result import Result, ResultReader
99

10-
ENHANCED_REPORT = os.environ.get('ENHANCED_REPORT', True)
1110

1211
def is_recursion_error(python_version: str, result: Result) -> bool:
1312
"""
@@ -378,35 +377,34 @@ def report(results_dir: str, minifier_ref: str, minifier_sha: str, base_ref: str
378377

379378
if base_summary is None:
380379
yield (
381-
f'| {python_version} ' +
382-
f'| {summary.valid_count} ' +
383-
f'| {summary.mean_time:.3f} ' +
384-
f'| {summary.mean_percent_of_original:.3f}% ' +
385-
f'| {len(list(summary.larger_than_original()))} ' +
386-
f'| {len(list(summary.recursion_error()))} ' +
387-
f'| {len(list(summary.unstable_minification()))} ' +
380+
f'| {python_version} '
381+
f'| {summary.valid_count} '
382+
f'| {summary.mean_time:.3f} '
383+
f'| {summary.mean_percent_of_original:.3f}% '
384+
f'| {len(list(summary.larger_than_original()))} '
385+
f'| {len(list(summary.recursion_error()))} '
386+
f'| {len(list(summary.unstable_minification()))} '
388387
f'| {len(list(summary.exception()))} |'
389388
)
390389
else:
391390
mean_time_change = summary.mean_time - base_summary.mean_time
392391

393392
yield (
394-
f'| {python_version} ' +
395-
f'| {summary.valid_count} ' +
396-
f'| {summary.mean_time:.3f} ({mean_time_change:+.3f}) ' +
397-
f'| {format_size_change_detail(summary, base_summary)} ' +
398-
f'| {format_difference(summary.larger_than_original(), base_summary.larger_than_original())} ' +
399-
f'| {format_difference(summary.recursion_error(), base_summary.recursion_error())} ' +
400-
f'| {format_difference(summary.unstable_minification(), base_summary.unstable_minification())} ' +
393+
f'| {python_version} '
394+
f'| {summary.valid_count} '
395+
f'| {summary.mean_time:.3f} ({mean_time_change:+.3f}) '
396+
f'| {format_size_change_detail(summary, base_summary)} '
397+
f'| {format_difference(summary.larger_than_original(), base_summary.larger_than_original())} '
398+
f'| {format_difference(summary.recursion_error(), base_summary.recursion_error())} '
399+
f'| {format_difference(summary.unstable_minification(), base_summary.unstable_minification())} '
401400
f'| {format_difference(summary.exception(), base_summary.exception())} |'
402401
)
403402

404-
if ENHANCED_REPORT:
405-
yield from report_larger_than_original(results_dir, ['3.14'], minifier_sha)
406-
yield from report_larger_than_base(results_dir, ['3.13'], minifier_sha, base_sha)
407-
yield from report_slowest(results_dir, ['3.14'], minifier_sha)
408-
yield from report_unstable(results_dir, ['2.7', '3.3', '3.4', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14'], minifier_sha)
409-
yield from report_exceptions(results_dir, ['2.7', '3.3', '3.4', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14'], minifier_sha)
403+
yield from report_larger_than_original(results_dir, ['3.14'], minifier_sha)
404+
yield from report_larger_than_base(results_dir, ['3.13'], minifier_sha, base_sha)
405+
yield from report_slowest(results_dir, ['3.14'], minifier_sha)
406+
yield from report_unstable(results_dir, ['2.7', '3.3', '3.4', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14'], minifier_sha)
407+
yield from report_exceptions(results_dir, ['2.7', '3.3', '3.4', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14'], minifier_sha)
410408

411409

412410
def main():

hypo_test/expressions.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ def Bytes(draw) -> ast.Constant:
8888

8989
@composite
9090
def List(draw, expression) -> ast.List:
91-
l = draw(lists(expression, min_size=0, max_size=3))
92-
return ast.List(elts=l, ctx=ast.Load())
91+
elements = draw(lists(expression, min_size=0, max_size=3))
92+
return ast.List(elts=elements, ctx=ast.Load())
9393

9494

9595
@composite
@@ -168,8 +168,8 @@ def Name(draw, ctx=ast.Load) -> ast.Name:
168168
@composite
169169
def UnaryOp(draw, expression) -> ast.UnaryOp:
170170
op = draw(sampled_from([ast.UAdd(), ast.USub(), ast.Not(), ast.Invert()]))
171-
l = draw(expression)
172-
return ast.UnaryOp(op, l)
171+
operand = draw(expression)
172+
return ast.UnaryOp(op, operand)
173173

174174

175175
@composite

hypo_test/module.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import python_minifier.ast_compat as ast
22

3-
from hypothesis import assume
43
from hypothesis.strategies import SearchStrategy, booleans, composite, integers, lists, none, one_of, recursive, sampled_from
54

65
from .expressions import Name, arguments, expression, name

hypo_test/patterns.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import keyword
33
import string
44

5-
from hypothesis import assume
65
from hypothesis.strategies import booleans, composite, integers, lists, none, one_of, recursive, sampled_from, text
76

87

@@ -35,22 +34,22 @@ def MatchStar(draw) -> ast.MatchStar:
3534

3635
@composite
3736
def MatchSequence(draw, pattern) -> ast.MatchSequence:
38-
l = draw(lists(pattern, min_size=1, max_size=3))
37+
patterns = draw(lists(pattern, min_size=1, max_size=3))
3938

4039
has_star = draw(booleans())
4140

4241
if has_star:
43-
star_pos = draw(integers(min_value=0, max_value=len(l)))
44-
l.insert(star_pos, draw(MatchStar()))
42+
star_pos = draw(integers(min_value=0, max_value=len(patterns)))
43+
patterns.insert(star_pos, draw(MatchStar()))
4544

46-
return ast.MatchSequence(patterns=l)
45+
return ast.MatchSequence(patterns=patterns)
4746

4847

4948
@composite
5049
def MatchMapping(draw, pattern) -> ast.MatchMapping:
51-
l = draw(lists(pattern, min_size=1, max_size=3))
50+
patterns = draw(lists(pattern, min_size=1, max_size=3))
5251

53-
match_mapping = ast.MatchMapping(keys=[ast.Constant(value=0) for i in range(len(l))], patterns=l)
52+
match_mapping = ast.MatchMapping(keys=[ast.Constant(value=0) for i in range(len(patterns))], patterns=patterns)
5453

5554
has_star = draw(booleans())
5655
if has_star:
@@ -88,8 +87,8 @@ def MatchAs(draw, pattern) -> ast.MatchAs:
8887

8988
@composite
9089
def MatchOr(draw, pattern) -> ast.MatchOr:
91-
l = draw(lists(pattern, min_size=2, max_size=3))
92-
return ast.MatchOr(patterns=l)
90+
patterns = draw(lists(pattern, min_size=2, max_size=3))
91+
return ast.MatchOr(patterns=patterns)
9392

9493

9594
leaves = one_of(

hypo_test/strings.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import ast
22

3-
from hypothesis import assume
43
from hypothesis.strategies import (
54
SearchStrategy,
65
booleans,

src/python_minifier/ast_compare.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def counter():
6363

6464
if field == 'kind' and isinstance(l_ast, ast.Constant):
6565
continue
66-
66+
6767
if field == 'str' and hasattr(ast, 'Interpolation') and isinstance(l_ast, ast.Interpolation):
6868
continue
6969

src/python_minifier/ast_compat.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,4 @@ def __new__(cls, *args, **kwargs):
7777
'withitem',
7878
]:
7979
if _node_type not in globals():
80-
globals()[_node_type] = type(_node_type, (AST,), {})
80+
globals()[_node_type] = type(_node_type, (AST,), {})

src/python_minifier/expression_printer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,8 +301,8 @@ def visit_BoolOp(self, node):
301301

302302
if value_precedence != 0 and (
303303
(op_precedence > value_precedence)
304-
or op_precedence == value_precedence
305-
and self._is_left_associative(node.op)
304+
or (op_precedence == value_precedence
305+
and self._is_left_associative(node.op))
306306
):
307307
self.printer.delimiter('(')
308308
self._expression(v)

0 commit comments

Comments
 (0)