sarj-python-lint 0.30.0__tar.gz → 0.31.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.
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/PKG-INFO +1 -1
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/pyproject.toml +1 -1
- sarj_python_lint-0.31.0/src/sarj_python_lint/rules/_paths.py +173 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/duplicate_test_body.py +3 -3
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/inefficient_string_concat_in_loop.py +3 -3
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/kwonly_same_type_params.py +3 -3
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_comment_cruft.py +3 -3
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_fat_try_blocks.py +3 -3
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_gen_random_uuid_in_sql.py +2 -2
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_repeated_string_literal.py +3 -3
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_restated_comment.py +2 -2
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_stdlib_logging.py +2 -2
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +3 -3
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +14 -2
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_module_level_constant.py +3 -3
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +3 -3
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +2 -6
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_timedelta_for_durations.py +3 -3
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/redundant_docstring.py +2 -2
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/require_port_for_service.py +2 -2
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/single_public_export.py +3 -3
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/stepdown.py +3 -3
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/trailing_value_narration.py +2 -2
- sarj_python_lint-0.30.0/src/sarj_python_lint/rules/_paths.py +0 -63
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/.gitignore +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/README.md +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/_comments.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/_registry.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/conditional_assertion_in_test.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/fixture_returns_bare_tuple.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/interaction_only_test.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/kwarg_heavy_construction_in_test.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_aggregation_in_store_query.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_file_level_escape_hatch_noqa.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_file_level_suppression.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_fstring_in_log.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_implicit_attribute_access.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_isinstance_union_chain.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_optional_tenant_predicate.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_patching_system_under_test.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_query_with_many_joins.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_raw_sql_in_tests.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_sentinel_return_on_except.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_sequential_await.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_sleep_in_test_body.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_unreachable_after_terminal.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/parametrize_case_needs_id.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_match_pattern_destructuring.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_real_store_in_tests.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/pydantic_at_boundaries.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/sleep_with_computed_arg_in_test.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/tautological_mock_assertion.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/test_loops_over_literal_cases.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/trivially_true_assertion.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/xfail_requires_strict.py +0 -0
- {sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/zero_assertion_test.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sarj-python-lint
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.31.0
|
|
4
4
|
Summary: Custom Python lint rules — AST-based, pre-commit-friendly, hypermodern defaults
|
|
5
5
|
Project-URL: Homepage, https://github.com/sarj-ai/standards/tree/main/packages/python
|
|
6
6
|
Project-URL: Repository, https://github.com/sarj-ai/standards
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""Shared file-scope predicates for the file-scope-gated rules.
|
|
2
|
+
|
|
3
|
+
A single definition of "is this a test file?" so the test-scoped rules
|
|
4
|
+
(SARJ031 no-sleep-in-test-body, SARJ036 no-raw-sql-in-tests) and the rules
|
|
5
|
+
that *exempt* tests (SARJ014 prefer-timedelta-for-durations, SARJ015
|
|
6
|
+
prefer-struct-over-namedtuple, SARJ026 prefer-namedtuple-over-tuple-return,
|
|
7
|
+
SARJ034 kwonly-same-type-params) never diverge on what counts as a test file.
|
|
8
|
+
|
|
9
|
+
Also the single definition of "is this a generated file?" (SARJ016/034):
|
|
10
|
+
generated code mirrors whatever its generator emits, so style rules cannot be
|
|
11
|
+
acted on there.
|
|
12
|
+
|
|
13
|
+
That definition is deliberately two-sided. `is_generated_source` reads the
|
|
14
|
+
header, which works for protoc/`@generated` tooling but is useless for the
|
|
15
|
+
generators that emit no banner at all. Measured on a checked-in
|
|
16
|
+
openapi-python-client SDK: 240 generated modules, *zero* carrying a marker in
|
|
17
|
+
their first five lines. `is_generated_path` covers those by looking at where
|
|
18
|
+
the file sits instead of what it says. `is_generated` is the predicate rules
|
|
19
|
+
should call — it is the union, and neither half alone is enough.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from functools import cache
|
|
25
|
+
import re
|
|
26
|
+
from typing import TYPE_CHECKING
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
_TEST_DIR_NAMES = frozenset({"tests", "test"})
|
|
34
|
+
|
|
35
|
+
# `auto-?generated` misses "automatically generated", which is the phrase
|
|
36
|
+
# @generated tooling, protoc and OpenAPI generators emit most often.
|
|
37
|
+
_GENERATED_RE = re.compile(
|
|
38
|
+
r"auto(?:-|matically )?generated|generated by|do not edit|@generated",
|
|
39
|
+
re.IGNORECASE,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
_GENERATED_HEADER_LINES = 5
|
|
43
|
+
|
|
44
|
+
_GENERATED_DIR_NAMES = frozenset({"generated", "vendor", "vendored"})
|
|
45
|
+
|
|
46
|
+
# A directory holding one of these is a code-generator root: the file is the
|
|
47
|
+
# generator's own config or ignore list, and the emitted package is checked in
|
|
48
|
+
# beneath it. Every entry is a filename published by a code generator —
|
|
49
|
+
# OpenAPI Generator, swagger-codegen, openapi-python-client, buf, and
|
|
50
|
+
# graphql-code-generator's `codegen.*` family. None is specific to any one
|
|
51
|
+
# repository's layout.
|
|
52
|
+
_CODEGEN_MARKER_NAMES = (
|
|
53
|
+
".openapi-generator",
|
|
54
|
+
".openapi-generator-ignore",
|
|
55
|
+
".swagger-codegen-ignore",
|
|
56
|
+
"openapi-python-client.yml",
|
|
57
|
+
"openapi-python-client.yaml",
|
|
58
|
+
"codegen.yml",
|
|
59
|
+
"codegen.yaml",
|
|
60
|
+
"codegen.config.yml",
|
|
61
|
+
"codegen.config.yaml",
|
|
62
|
+
"buf.gen.yaml",
|
|
63
|
+
"buf.gen.yml",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Ancestor walks stop at the repository root. Without this a file two levels
|
|
67
|
+
# deep would stat every directory up to `/` looking for a marker.
|
|
68
|
+
_REPO_ROOT_MARKERS = (".git",)
|
|
69
|
+
|
|
70
|
+
# Guards against a pathological path (or a symlink loop) turning the ancestor
|
|
71
|
+
# walk into an unbounded stat storm. No real source tree is this deep.
|
|
72
|
+
_MAX_ANCESTOR_DEPTH = 40
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def is_generated_source(source: str) -> bool:
|
|
76
|
+
"""Report whether `source` self-identifies as generated code.
|
|
77
|
+
|
|
78
|
+
Checks the first few lines for the conventional markers
|
|
79
|
+
("autogenerated", "generated by", "do not edit").
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
True when the file header carries a generated-code marker.
|
|
83
|
+
|
|
84
|
+
"""
|
|
85
|
+
head = source.splitlines()[:_GENERATED_HEADER_LINES]
|
|
86
|
+
return any(_GENERATED_RE.search(line) for line in head)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@cache
|
|
90
|
+
def _is_codegen_root(directory: Path) -> bool:
|
|
91
|
+
"""Report whether `directory` holds a code generator's config or ignore file.
|
|
92
|
+
|
|
93
|
+
Cached: a corpus run asks this of the same handful of directories once per
|
|
94
|
+
file, and the answer is a filesystem probe.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
True when the directory carries a generator marker file.
|
|
98
|
+
|
|
99
|
+
"""
|
|
100
|
+
return any((directory / name).exists() for name in _CODEGEN_MARKER_NAMES)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@cache
|
|
104
|
+
def _is_repo_root(directory: Path) -> bool:
|
|
105
|
+
"""Report whether `directory` is a repository root.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
True when the directory carries a repository marker (`.git`).
|
|
109
|
+
|
|
110
|
+
"""
|
|
111
|
+
return any((directory / name).exists() for name in _REPO_ROOT_MARKERS)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def is_generated_path(path: Path) -> bool:
|
|
115
|
+
"""Report whether `path` sits in a tree of generated or vendored code.
|
|
116
|
+
|
|
117
|
+
Two signals, both about location rather than content:
|
|
118
|
+
|
|
119
|
+
* a `generated` / `vendor` / `vendored` directory anywhere in the path, and
|
|
120
|
+
* a code-generator root among the ancestors — a directory holding an
|
|
121
|
+
`.openapi-generator-ignore`, `codegen.config.yml` or similar.
|
|
122
|
+
|
|
123
|
+
The generator root itself stays linted: the config file's neighbours are
|
|
124
|
+
the hand-written driver script that invokes the generator, while the
|
|
125
|
+
emitted package is checked in one level down. Only the subtree below the
|
|
126
|
+
root is treated as generated.
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
True when the file is machine-written output rather than source.
|
|
130
|
+
|
|
131
|
+
"""
|
|
132
|
+
if any(part.lower() in _GENERATED_DIR_NAMES for part in path.parts):
|
|
133
|
+
return True
|
|
134
|
+
for depth, ancestor in enumerate(path.parents):
|
|
135
|
+
if depth >= _MAX_ANCESTOR_DEPTH:
|
|
136
|
+
break
|
|
137
|
+
# `depth == 0` is the file's own directory: a generator config sitting
|
|
138
|
+
# next to the file makes the file the generator, not its output.
|
|
139
|
+
if depth and _is_codegen_root(ancestor):
|
|
140
|
+
return True
|
|
141
|
+
if _is_repo_root(ancestor):
|
|
142
|
+
break
|
|
143
|
+
return False
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def is_generated(path: Path, source: str) -> bool:
|
|
147
|
+
"""Report whether the file is generated, by either header or location.
|
|
148
|
+
|
|
149
|
+
The predicate every rule that exempts generated code should call. Checking
|
|
150
|
+
only the header misses banner-less generators; checking only the path
|
|
151
|
+
misses a generated file dropped into a hand-written tree.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
True when the file is machine-written output rather than source.
|
|
155
|
+
|
|
156
|
+
"""
|
|
157
|
+
return is_generated_source(source) or is_generated_path(path)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def is_test_path(path: Path) -> bool:
|
|
161
|
+
"""Report whether `path` is a test file.
|
|
162
|
+
|
|
163
|
+
A test file is `conftest.py`, a `test_*.py` / `*_test.py` stem, or any file
|
|
164
|
+
under a `tests` / `test` directory segment.
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
True when `path` belongs to the test tree.
|
|
168
|
+
|
|
169
|
+
"""
|
|
170
|
+
name = path.name
|
|
171
|
+
if name == "conftest.py" or name.startswith("test_") or name.endswith("_test.py"):
|
|
172
|
+
return True
|
|
173
|
+
return any(part in _TEST_DIR_NAMES for part in path.parts)
|
|
@@ -182,7 +182,7 @@ Deliberately NOT flagged:
|
|
|
182
182
|
* **short bodies.** A two-statement test is not evidence of copy-paste — call,
|
|
183
183
|
assert, done, and any two tests of the same helper look alike. The 3-statement
|
|
184
184
|
floor is what keeps the rule from firing on every well-factored unit suite,
|
|
185
|
-
* **generated test modules** (`
|
|
185
|
+
* **generated test modules** (`_paths.is_generated`) — a generator emitting N
|
|
186
186
|
near-identical cases is the generator's business, and the fix would be
|
|
187
187
|
overwritten on the next regeneration,
|
|
188
188
|
* the **first** member of a group. The diagnostic goes on the copies and points
|
|
@@ -200,7 +200,7 @@ from typing import TYPE_CHECKING, override
|
|
|
200
200
|
from sarj_python_lint.rule_base import Diagnostic, Rule, parse_or_none
|
|
201
201
|
from sarj_python_lint.rules._ast_index import children
|
|
202
202
|
from sarj_python_lint.rules._comments import standalone_comments, trailing_comments
|
|
203
|
-
from sarj_python_lint.rules._paths import
|
|
203
|
+
from sarj_python_lint.rules._paths import is_generated, is_test_path
|
|
204
204
|
|
|
205
205
|
|
|
206
206
|
if TYPE_CHECKING:
|
|
@@ -286,7 +286,7 @@ class DuplicateTestBody(Rule):
|
|
|
286
286
|
by position.
|
|
287
287
|
|
|
288
288
|
"""
|
|
289
|
-
if not is_test_path(path) or
|
|
289
|
+
if not is_test_path(path) or is_generated(path, source):
|
|
290
290
|
return []
|
|
291
291
|
tree = parse_or_none(path, source)
|
|
292
292
|
if tree is None:
|
|
@@ -34,7 +34,7 @@ References:
|
|
|
34
34
|
- https://docs.python.org/3/library/stdtypes.html#str.join
|
|
35
35
|
- https://wiki.python.org/moin/PythonSpeed/PerformanceTips
|
|
36
36
|
|
|
37
|
-
* **generated files** (`_paths.
|
|
37
|
+
* **generated files** (`_paths.is_generated`). Their layout is the
|
|
38
38
|
generator's, and re-running the generator discards any edit, so a finding
|
|
39
39
|
there can never be acted on in place. Measured on the 69 `DO NOT EDIT`
|
|
40
40
|
files git-tracked across bulbul and noura-be — Speakeasy's
|
|
@@ -49,7 +49,7 @@ from typing import TYPE_CHECKING, TypeGuard, override
|
|
|
49
49
|
|
|
50
50
|
from sarj_python_lint.rule_base import Diagnostic, Rule, parse_or_none
|
|
51
51
|
from sarj_python_lint.rules._ast_index import children, walk
|
|
52
|
-
from sarj_python_lint.rules._paths import
|
|
52
|
+
from sarj_python_lint.rules._paths import is_generated
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
if TYPE_CHECKING:
|
|
@@ -66,7 +66,7 @@ class InefficientStringConcatInLoop(Rule):
|
|
|
66
66
|
|
|
67
67
|
@override
|
|
68
68
|
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
69
|
-
if
|
|
69
|
+
if is_generated(path, source):
|
|
70
70
|
return []
|
|
71
71
|
if "+" not in source or ("for " not in source and "while " not in source):
|
|
72
72
|
return []
|
|
@@ -42,7 +42,7 @@ Never flags — these signatures cannot or should not change:
|
|
|
42
42
|
`black/scripts/diff_shades_gha_helper.py:167`),
|
|
43
43
|
* test files (`_paths.is_test_path`) — test fakes and helpers mirror the
|
|
44
44
|
signatures of the code under test and cannot unilaterally change them,
|
|
45
|
-
* generated files (`_paths.
|
|
45
|
+
* generated files (`_paths.is_generated`) — the signature mirrors
|
|
46
46
|
whatever the generator emits (found via trio's `_generated_io_kqueue.py`),
|
|
47
47
|
* functions whose name is referenced as a VALUE anywhere in the module
|
|
48
48
|
(passed to a registry, returned, stored) — the signature is a callback
|
|
@@ -101,7 +101,7 @@ from typing import TYPE_CHECKING, override
|
|
|
101
101
|
|
|
102
102
|
from sarj_python_lint.rule_base import Diagnostic, Rule, parse_or_none
|
|
103
103
|
from sarj_python_lint.rules._ast_index import nodes, walk
|
|
104
|
-
from sarj_python_lint.rules._paths import
|
|
104
|
+
from sarj_python_lint.rules._paths import is_generated, is_test_path
|
|
105
105
|
|
|
106
106
|
|
|
107
107
|
if TYPE_CHECKING:
|
|
@@ -196,7 +196,7 @@ class KwonlySameTypeParams(Rule):
|
|
|
196
196
|
|
|
197
197
|
@override
|
|
198
198
|
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
199
|
-
if is_test_path(path) or
|
|
199
|
+
if is_test_path(path) or is_generated(path, source):
|
|
200
200
|
return []
|
|
201
201
|
tree = parse_or_none(path, source)
|
|
202
202
|
if tree is None:
|
{sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_comment_cruft.py
RENAMED
|
@@ -66,7 +66,7 @@ comments — `# type:`, `# noqa`, `# sarj-noqa`,
|
|
|
66
66
|
coding declarations.
|
|
67
67
|
|
|
68
68
|
Also NOT flagged (famous-repo sweep hardening):
|
|
69
|
-
- generated files (`_paths.
|
|
69
|
+
- generated files (`_paths.is_generated`) — their banners are the
|
|
70
70
|
generator's warning header, not hand-written cruft;
|
|
71
71
|
- a punctuation-only "banner" directly beneath a texty comment line — that is
|
|
72
72
|
an RST-style heading underline or an ASCII-diagram row inside a prose comment
|
|
@@ -153,7 +153,7 @@ from sarj_python_lint.rules._comments import (
|
|
|
153
153
|
nested_comment_lines,
|
|
154
154
|
standalone_comments,
|
|
155
155
|
)
|
|
156
|
-
from sarj_python_lint.rules._paths import
|
|
156
|
+
from sarj_python_lint.rules._paths import is_generated
|
|
157
157
|
|
|
158
158
|
|
|
159
159
|
if TYPE_CHECKING:
|
|
@@ -683,7 +683,7 @@ class NoCommentCruft(Rule):
|
|
|
683
683
|
|
|
684
684
|
@override
|
|
685
685
|
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
686
|
-
if
|
|
686
|
+
if is_generated(path, source):
|
|
687
687
|
return []
|
|
688
688
|
# A Sphinx `docs/**/conf.py` is quickstart-generated boilerplate whose
|
|
689
689
|
# `# -- Section ----` banners are the tool's own convention.
|
{sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_fat_try_blocks.py
RENAMED
|
@@ -69,7 +69,7 @@ References:
|
|
|
69
69
|
- https://docs.python.org/3/tutorial/errors.html#handling-exceptions
|
|
70
70
|
- https://docs.python.org/3/library/ast.html#ast.Try
|
|
71
71
|
|
|
72
|
-
* **generated files** (`_paths.
|
|
72
|
+
* **generated files** (`_paths.is_generated`). Their layout is the
|
|
73
73
|
generator's, and re-running the generator discards any edit, so a finding
|
|
74
74
|
there can never be acted on in place. Measured on the 69 `DO NOT EDIT`
|
|
75
75
|
files git-tracked across bulbul and noura-be — Speakeasy's
|
|
@@ -86,7 +86,7 @@ from typing import TYPE_CHECKING, override
|
|
|
86
86
|
from sarj_python_lint.rule_base import Diagnostic, Rule, parse_or_none
|
|
87
87
|
from sarj_python_lint.rules._ast_index import children, nodes
|
|
88
88
|
from sarj_python_lint.rules._logging import is_logger_expr
|
|
89
|
-
from sarj_python_lint.rules._paths import
|
|
89
|
+
from sarj_python_lint.rules._paths import is_generated
|
|
90
90
|
|
|
91
91
|
|
|
92
92
|
if TYPE_CHECKING:
|
|
@@ -330,7 +330,7 @@ class NoFatTryBlocks(Rule):
|
|
|
330
330
|
|
|
331
331
|
@override
|
|
332
332
|
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
333
|
-
if
|
|
333
|
+
if is_generated(path, source):
|
|
334
334
|
return []
|
|
335
335
|
tree = parse_or_none(path, source)
|
|
336
336
|
if tree is None:
|
|
@@ -56,7 +56,7 @@ from typing import TYPE_CHECKING, final, override
|
|
|
56
56
|
|
|
57
57
|
from sarj_python_lint.rule_base import Diagnostic, Rule, parse_or_none
|
|
58
58
|
from sarj_python_lint.rules._ast_index import nodes
|
|
59
|
-
from sarj_python_lint.rules._paths import
|
|
59
|
+
from sarj_python_lint.rules._paths import is_generated
|
|
60
60
|
from sarj_python_lint.rules._sql import strip_sql_noise
|
|
61
61
|
|
|
62
62
|
|
|
@@ -111,7 +111,7 @@ class NoGenRandomUuidInSql(Rule):
|
|
|
111
111
|
The diagnostics, sorted by (line, col).
|
|
112
112
|
|
|
113
113
|
"""
|
|
114
|
-
if
|
|
114
|
+
if is_generated(path, source):
|
|
115
115
|
return []
|
|
116
116
|
tree = parse_or_none(path, source)
|
|
117
117
|
if tree is None:
|
|
@@ -84,7 +84,7 @@ duplicate can be suppressed per-line with `# sarj-noqa: SARJ024 — <reason>`.
|
|
|
84
84
|
|
|
85
85
|
Skipped entirely: `conftest.py`, test files (`test_*.py` or under a `tests/`
|
|
86
86
|
directory) — fixtures legitimately repeat literal payloads.
|
|
87
|
-
* **generated files** (`_paths.
|
|
87
|
+
* **generated files** (`_paths.is_generated`). Their layout is the
|
|
88
88
|
generator's, and re-running the generator discards any edit, so a finding
|
|
89
89
|
there can never be acted on in place. Measured on the 69 `DO NOT EDIT`
|
|
90
90
|
files git-tracked across bulbul and noura-be — Speakeasy's
|
|
@@ -100,7 +100,7 @@ from typing import TYPE_CHECKING, override
|
|
|
100
100
|
|
|
101
101
|
from sarj_python_lint.rule_base import Diagnostic, Rule, parse_or_none
|
|
102
102
|
from sarj_python_lint.rules._ast_index import children, walk
|
|
103
|
-
from sarj_python_lint.rules._paths import
|
|
103
|
+
from sarj_python_lint.rules._paths import is_generated
|
|
104
104
|
|
|
105
105
|
|
|
106
106
|
if TYPE_CHECKING:
|
|
@@ -130,7 +130,7 @@ class NoRepeatedStringLiteral(Rule):
|
|
|
130
130
|
|
|
131
131
|
@override
|
|
132
132
|
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
133
|
-
if
|
|
133
|
+
if is_generated(path, source):
|
|
134
134
|
return []
|
|
135
135
|
if _is_skipped_path(path):
|
|
136
136
|
return []
|
|
@@ -105,7 +105,7 @@ from sarj_python_lint.rules._comments import (
|
|
|
105
105
|
restates,
|
|
106
106
|
standalone_comments,
|
|
107
107
|
)
|
|
108
|
-
from sarj_python_lint.rules._paths import
|
|
108
|
+
from sarj_python_lint.rules._paths import is_generated
|
|
109
109
|
|
|
110
110
|
|
|
111
111
|
if TYPE_CHECKING:
|
|
@@ -299,7 +299,7 @@ class NoRestatedComment(Rule):
|
|
|
299
299
|
|
|
300
300
|
@override
|
|
301
301
|
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
302
|
-
if
|
|
302
|
+
if is_generated(path, source):
|
|
303
303
|
return []
|
|
304
304
|
try:
|
|
305
305
|
standalone, _ = standalone_comments(source)
|
{sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_stdlib_logging.py
RENAMED
|
@@ -62,7 +62,7 @@ from typing import TYPE_CHECKING, final, override
|
|
|
62
62
|
|
|
63
63
|
from sarj_python_lint.rule_base import Diagnostic, Rule, parse_or_none
|
|
64
64
|
from sarj_python_lint.rules._ast_index import nodes, walk
|
|
65
|
-
from sarj_python_lint.rules._paths import
|
|
65
|
+
from sarj_python_lint.rules._paths import is_generated, is_test_path
|
|
66
66
|
|
|
67
67
|
|
|
68
68
|
if TYPE_CHECKING:
|
|
@@ -113,7 +113,7 @@ class NoStdlibLogging(Rule):
|
|
|
113
113
|
The diagnostics, sorted by (line, col).
|
|
114
114
|
|
|
115
115
|
"""
|
|
116
|
-
if is_test_path(path) or _EXEMPT_DIR_NAMES.intersection(path.parts) or
|
|
116
|
+
if is_test_path(path) or _EXEMPT_DIR_NAMES.intersection(path.parts) or is_generated(path, source):
|
|
117
117
|
return []
|
|
118
118
|
# Every diagnostic comes from an `import logging...` statement, which
|
|
119
119
|
# cannot exist unless the module name is spelled in the text. Checking
|
|
@@ -206,7 +206,7 @@ Deliberately NOT flagged:
|
|
|
206
206
|
f-string" would restyle an injection instead of fixing it, and this rule must
|
|
207
207
|
not become a competing SQL rule. The keyword match is case-SENSITIVE, so
|
|
208
208
|
prose like `"copied from " + src` is not mistaken for a query. 7 hits,
|
|
209
|
-
* **generated files** (`_paths.
|
|
209
|
+
* **generated files** (`_paths.is_generated`). Re-running the generator
|
|
210
210
|
discards any edit, so a style finding there can never be acted on — the same
|
|
211
211
|
exemption SARJ002 makes. 5 hits, all in bulbul's Speakeasy
|
|
212
212
|
`python/sdk/src/sarj_platform_sdk/`.
|
|
@@ -229,7 +229,7 @@ from typing import TYPE_CHECKING, override
|
|
|
229
229
|
from sarj_python_lint.rule_base import Diagnostic, Rule, parse_or_none
|
|
230
230
|
from sarj_python_lint.rules._ast_index import nodes, walk
|
|
231
231
|
from sarj_python_lint.rules._logging import is_logger_expr
|
|
232
|
-
from sarj_python_lint.rules._paths import
|
|
232
|
+
from sarj_python_lint.rules._paths import is_generated
|
|
233
233
|
|
|
234
234
|
|
|
235
235
|
if TYPE_CHECKING:
|
|
@@ -326,7 +326,7 @@ class PreferFstringOverConcat(Rule):
|
|
|
326
326
|
One diagnostic per outermost offending concatenation, sorted by position.
|
|
327
327
|
|
|
328
328
|
"""
|
|
329
|
-
if "+" not in source or
|
|
329
|
+
if "+" not in source or is_generated(path, source):
|
|
330
330
|
return []
|
|
331
331
|
tree = parse_or_none(path, source)
|
|
332
332
|
if tree is None:
|
|
@@ -27,6 +27,18 @@ Example refactoring:
|
|
|
27
27
|
case dict():
|
|
28
28
|
return parse_dict(data)
|
|
29
29
|
return cast(..., data)
|
|
30
|
+
|
|
31
|
+
## Not reported
|
|
32
|
+
|
|
33
|
+
* **generated files** (`_paths.is_generated`). This is the exemption that
|
|
34
|
+
matters most for this rule: the try/raise idiom in the module summary above
|
|
35
|
+
is *transcribed from* openapi-python-client's `_parse_*` template, and it
|
|
36
|
+
reproduces once per nullable field. Before the path half of `is_generated`
|
|
37
|
+
existed, 314 of this rule's 334 findings over two first-party corpora came
|
|
38
|
+
from a single checked-in SDK — a tree the consuming repo already excludes
|
|
39
|
+
from ruff, from its pre-commit hook and from its CI invocation. The
|
|
40
|
+
generator's output is not a refactor anyone can accept; re-running the
|
|
41
|
+
generator would undo it. The 20 findings that remain are hand-written.
|
|
30
42
|
"""
|
|
31
43
|
|
|
32
44
|
from __future__ import annotations
|
|
@@ -35,7 +47,7 @@ import ast
|
|
|
35
47
|
from typing import TYPE_CHECKING, final, override
|
|
36
48
|
|
|
37
49
|
from sarj_python_lint.rule_base import Diagnostic, Rule, parse_or_none
|
|
38
|
-
from sarj_python_lint.rules._paths import
|
|
50
|
+
from sarj_python_lint.rules._paths import is_generated
|
|
39
51
|
|
|
40
52
|
|
|
41
53
|
if TYPE_CHECKING:
|
|
@@ -263,7 +275,7 @@ class PreferMatchTypeDispatch(Rule):
|
|
|
263
275
|
|
|
264
276
|
@override
|
|
265
277
|
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
266
|
-
if
|
|
278
|
+
if is_generated(path, source):
|
|
267
279
|
return []
|
|
268
280
|
tree = parse_or_none(path, source)
|
|
269
281
|
if tree is None:
|
|
@@ -69,7 +69,7 @@ Deliberately NOT flagged:
|
|
|
69
69
|
so they are reads, while `x.sort()` mutates in place and bails.
|
|
70
70
|
* **Tiny displays** (`< _MIN_ELEMENTS` entries) read better next to their use.
|
|
71
71
|
* **Test files and generated files** (`_paths.is_test_path` /
|
|
72
|
-
`_paths.
|
|
72
|
+
`_paths.is_generated`): fixture tables belong next to the assertion
|
|
73
73
|
that explains them, and generated code mirrors its generator.
|
|
74
74
|
* **Bindings the function never reads** and **functions that call `locals()` /
|
|
75
75
|
`vars()`**. Both are the same hole in the escape analysis: a local can leave
|
|
@@ -104,7 +104,7 @@ from typing import TYPE_CHECKING, override
|
|
|
104
104
|
|
|
105
105
|
from sarj_python_lint.rule_base import Diagnostic, Rule, parse_or_none
|
|
106
106
|
from sarj_python_lint.rules._ast_index import children, nodes
|
|
107
|
-
from sarj_python_lint.rules._paths import
|
|
107
|
+
from sarj_python_lint.rules._paths import is_generated, is_test_path
|
|
108
108
|
|
|
109
109
|
|
|
110
110
|
if TYPE_CHECKING:
|
|
@@ -194,7 +194,7 @@ class PreferModuleLevelConstant(Rule):
|
|
|
194
194
|
|
|
195
195
|
@override
|
|
196
196
|
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
197
|
-
if is_test_path(path) or
|
|
197
|
+
if is_test_path(path) or is_generated(path, source):
|
|
198
198
|
return []
|
|
199
199
|
tree = parse_or_none(path, source)
|
|
200
200
|
if tree is None:
|
|
@@ -70,7 +70,7 @@ Suppress a deliberate positional return with `# sarj-noqa: SARJ026 — <reason>`
|
|
|
70
70
|
References:
|
|
71
71
|
- https://docs.python.org/3/library/typing.html#typing.NamedTuple
|
|
72
72
|
|
|
73
|
-
* **generated files** (`_paths.
|
|
73
|
+
* **generated files** (`_paths.is_generated`). Their layout is the
|
|
74
74
|
generator's, and re-running the generator discards any edit, so a finding
|
|
75
75
|
there can never be acted on in place. Measured on the 69 `DO NOT EDIT`
|
|
76
76
|
files git-tracked across bulbul and noura-be — Speakeasy's
|
|
@@ -87,7 +87,7 @@ from typing import TYPE_CHECKING, override
|
|
|
87
87
|
|
|
88
88
|
from sarj_python_lint.rule_base import Diagnostic, Rule, parse_or_none
|
|
89
89
|
from sarj_python_lint.rules._ast_index import children, nodes, walk
|
|
90
|
-
from sarj_python_lint.rules._paths import
|
|
90
|
+
from sarj_python_lint.rules._paths import is_generated, is_test_path
|
|
91
91
|
|
|
92
92
|
|
|
93
93
|
if TYPE_CHECKING:
|
|
@@ -147,7 +147,7 @@ class PreferNamedtupleOverTupleReturn(Rule):
|
|
|
147
147
|
|
|
148
148
|
@override
|
|
149
149
|
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
150
|
-
if
|
|
150
|
+
if is_generated(path, source):
|
|
151
151
|
return []
|
|
152
152
|
if is_test_path(path):
|
|
153
153
|
return []
|
|
@@ -40,7 +40,7 @@ from typing import TYPE_CHECKING, override
|
|
|
40
40
|
|
|
41
41
|
from sarj_python_lint.rule_base import Diagnostic, Rule, parse_or_none
|
|
42
42
|
from sarj_python_lint.rules._ast_index import walk
|
|
43
|
-
from sarj_python_lint.rules._paths import
|
|
43
|
+
from sarj_python_lint.rules._paths import is_generated, is_test_path
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
if TYPE_CHECKING:
|
|
@@ -61,7 +61,7 @@ class PreferNonNullableCollection(Rule):
|
|
|
61
61
|
|
|
62
62
|
@override
|
|
63
63
|
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
64
|
-
if is_test_path(path) or
|
|
64
|
+
if is_test_path(path) or is_generated(path, source):
|
|
65
65
|
return []
|
|
66
66
|
tree = parse_or_none(path, source)
|
|
67
67
|
if tree is None:
|
|
@@ -94,10 +94,6 @@ class PreferNonNullableCollection(Rule):
|
|
|
94
94
|
return diags
|
|
95
95
|
|
|
96
96
|
|
|
97
|
-
def _is_generated_or_vendored_path(path: Path) -> bool:
|
|
98
|
-
return any(part.lower() in {"generated", "vendor", "vendored"} for part in path.parts)
|
|
99
|
-
|
|
100
|
-
|
|
101
97
|
def _qualified_name(node: ast.expr) -> str:
|
|
102
98
|
if isinstance(node, ast.Name):
|
|
103
99
|
return node.id
|
|
@@ -71,7 +71,7 @@ Suppress an intentional raw-numeric duration with `# sarj-noqa: SARJ014 — <rea
|
|
|
71
71
|
References:
|
|
72
72
|
- https://docs.python.org/3/library/datetime.html#timedelta-objects
|
|
73
73
|
|
|
74
|
-
* **generated files** (`_paths.
|
|
74
|
+
* **generated files** (`_paths.is_generated`). Their layout is the
|
|
75
75
|
generator's, and re-running the generator discards any edit, so a finding
|
|
76
76
|
there can never be acted on in place. Measured on the 69 `DO NOT EDIT`
|
|
77
77
|
files git-tracked across bulbul and noura-be — Speakeasy's
|
|
@@ -87,7 +87,7 @@ from typing import TYPE_CHECKING, override
|
|
|
87
87
|
|
|
88
88
|
from sarj_python_lint.rule_base import Diagnostic, Rule, parse_or_none
|
|
89
89
|
from sarj_python_lint.rules._ast_index import nodes
|
|
90
|
-
from sarj_python_lint.rules._paths import
|
|
90
|
+
from sarj_python_lint.rules._paths import is_generated, is_test_path
|
|
91
91
|
|
|
92
92
|
|
|
93
93
|
if TYPE_CHECKING:
|
|
@@ -164,7 +164,7 @@ class PreferTimedeltaForDurations(Rule):
|
|
|
164
164
|
|
|
165
165
|
@override
|
|
166
166
|
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
167
|
-
if
|
|
167
|
+
if is_generated(path, source):
|
|
168
168
|
return []
|
|
169
169
|
if is_test_path(path):
|
|
170
170
|
return []
|
|
@@ -55,7 +55,7 @@ from typing import TYPE_CHECKING, override
|
|
|
55
55
|
from sarj_python_lint.rule_base import Diagnostic, Rule, parse_or_none
|
|
56
56
|
from sarj_python_lint.rules._ast_index import children
|
|
57
57
|
from sarj_python_lint.rules._comments import is_protected, split_identifier, stem
|
|
58
|
-
from sarj_python_lint.rules._paths import
|
|
58
|
+
from sarj_python_lint.rules._paths import is_generated
|
|
59
59
|
|
|
60
60
|
|
|
61
61
|
if TYPE_CHECKING:
|
|
@@ -239,7 +239,7 @@ class RedundantDocstring(Rule):
|
|
|
239
239
|
|
|
240
240
|
@override
|
|
241
241
|
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
242
|
-
if
|
|
242
|
+
if is_generated(path, source):
|
|
243
243
|
return []
|
|
244
244
|
tree = parse_or_none(path, source)
|
|
245
245
|
if tree is None:
|
|
@@ -266,7 +266,7 @@ import re
|
|
|
266
266
|
from typing import TYPE_CHECKING, override
|
|
267
267
|
|
|
268
268
|
from sarj_python_lint.rule_base import Diagnostic, Rule, parse_or_none
|
|
269
|
-
from sarj_python_lint.rules._paths import
|
|
269
|
+
from sarj_python_lint.rules._paths import is_generated, is_test_path
|
|
270
270
|
|
|
271
271
|
|
|
272
272
|
if TYPE_CHECKING:
|
|
@@ -429,7 +429,7 @@ class RequirePortForService(Rule):
|
|
|
429
429
|
One diagnostic per unsubstitutable service class, sorted by position.
|
|
430
430
|
|
|
431
431
|
"""
|
|
432
|
-
if not _is_library_source(path) or
|
|
432
|
+
if not _is_library_source(path) or is_generated(path, source):
|
|
433
433
|
return []
|
|
434
434
|
tree = parse_or_none(path, source)
|
|
435
435
|
if tree is None:
|
|
@@ -39,7 +39,7 @@ a `tests/` directory), and framework-convention filenames whose stem is fixed by
|
|
|
39
39
|
a framework/tool and cannot be renamed (`models.py`, `views.py`, `base.py`, ...).
|
|
40
40
|
Modules whose single export already snake-cases to the stem are not flagged
|
|
41
41
|
(there is nothing to improve).
|
|
42
|
-
* **generated files** (`_paths.
|
|
42
|
+
* **generated files** (`_paths.is_generated`). Their layout is the
|
|
43
43
|
generator's, and re-running the generator discards any edit, so a finding
|
|
44
44
|
there can never be acted on in place. Measured on the 69 `DO NOT EDIT`
|
|
45
45
|
files git-tracked across bulbul and noura-be — Speakeasy's
|
|
@@ -53,7 +53,7 @@ import re
|
|
|
53
53
|
from typing import TYPE_CHECKING, override
|
|
54
54
|
|
|
55
55
|
from sarj_python_lint.rule_base import Diagnostic, Rule, parse_or_none
|
|
56
|
-
from sarj_python_lint.rules._paths import
|
|
56
|
+
from sarj_python_lint.rules._paths import is_generated
|
|
57
57
|
|
|
58
58
|
|
|
59
59
|
if TYPE_CHECKING:
|
|
@@ -134,7 +134,7 @@ class SinglePublicExport(Rule):
|
|
|
134
134
|
|
|
135
135
|
@override
|
|
136
136
|
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
137
|
-
if
|
|
137
|
+
if is_generated(path, source):
|
|
138
138
|
return []
|
|
139
139
|
if _is_skipped_path(path):
|
|
140
140
|
return []
|
|
@@ -58,7 +58,7 @@ Never fires on:
|
|
|
58
58
|
`_code_str`). Siblings are excluded — an identically-named sibling method is a
|
|
59
59
|
different method. Callers in classes outside the module remain invisible to
|
|
60
60
|
syntactic analysis.
|
|
61
|
-
* **generated files** (`_paths.
|
|
61
|
+
* **generated files** (`_paths.is_generated`). Their layout is the
|
|
62
62
|
generator's, and re-running the generator discards any edit, so a finding
|
|
63
63
|
there can never be acted on in place. Measured on the 69 `DO NOT EDIT`
|
|
64
64
|
files git-tracked across bulbul and noura-be — Speakeasy's
|
|
@@ -73,7 +73,7 @@ from typing import TYPE_CHECKING, override
|
|
|
73
73
|
|
|
74
74
|
from sarj_python_lint.rule_base import Diagnostic, Rule, parse_or_none
|
|
75
75
|
from sarj_python_lint.rules._ast_index import children
|
|
76
|
-
from sarj_python_lint.rules._paths import
|
|
76
|
+
from sarj_python_lint.rules._paths import is_generated
|
|
77
77
|
|
|
78
78
|
|
|
79
79
|
if TYPE_CHECKING:
|
|
@@ -112,7 +112,7 @@ class Stepdown(Rule):
|
|
|
112
112
|
|
|
113
113
|
@override
|
|
114
114
|
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
115
|
-
if
|
|
115
|
+
if is_generated(path, source):
|
|
116
116
|
return []
|
|
117
117
|
if _is_test_path(path):
|
|
118
118
|
return []
|
|
@@ -69,7 +69,7 @@ from sarj_python_lint.rules._comments import (
|
|
|
69
69
|
stem,
|
|
70
70
|
trailing_comments,
|
|
71
71
|
)
|
|
72
|
-
from sarj_python_lint.rules._paths import
|
|
72
|
+
from sarj_python_lint.rules._paths import is_generated
|
|
73
73
|
|
|
74
74
|
|
|
75
75
|
if TYPE_CHECKING:
|
|
@@ -191,7 +191,7 @@ class TrailingValueNarration(Rule):
|
|
|
191
191
|
|
|
192
192
|
@override
|
|
193
193
|
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
194
|
-
if
|
|
194
|
+
if is_generated(path, source):
|
|
195
195
|
return []
|
|
196
196
|
try:
|
|
197
197
|
trailing = trailing_comments(source)
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
"""Shared file-scope predicates for the file-scope-gated rules.
|
|
2
|
-
|
|
3
|
-
A single definition of "is this a test file?" so the test-scoped rules
|
|
4
|
-
(SARJ031 no-sleep-in-test-body, SARJ036 no-raw-sql-in-tests) and the rules
|
|
5
|
-
that *exempt* tests (SARJ014 prefer-timedelta-for-durations, SARJ015
|
|
6
|
-
prefer-struct-over-namedtuple, SARJ026 prefer-namedtuple-over-tuple-return,
|
|
7
|
-
SARJ034 kwonly-same-type-params) never diverge on what counts as a test file.
|
|
8
|
-
|
|
9
|
-
Also the single definition of "is this a generated file?" (SARJ016/034):
|
|
10
|
-
generated code mirrors whatever its generator emits, so style rules cannot be
|
|
11
|
-
acted on there.
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
from __future__ import annotations
|
|
15
|
-
|
|
16
|
-
import re
|
|
17
|
-
from typing import TYPE_CHECKING
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if TYPE_CHECKING:
|
|
21
|
-
from pathlib import Path
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
_TEST_DIR_NAMES = frozenset({"tests", "test"})
|
|
25
|
-
|
|
26
|
-
# `auto-?generated` misses "automatically generated", which is the phrase
|
|
27
|
-
# @generated tooling, protoc and OpenAPI generators emit most often.
|
|
28
|
-
_GENERATED_RE = re.compile(
|
|
29
|
-
r"auto(?:-|matically )?generated|generated by|do not edit|@generated",
|
|
30
|
-
re.IGNORECASE,
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
_GENERATED_HEADER_LINES = 5
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def is_generated_source(source: str) -> bool:
|
|
37
|
-
"""Report whether `source` self-identifies as generated code.
|
|
38
|
-
|
|
39
|
-
Checks the first few lines for the conventional markers
|
|
40
|
-
("autogenerated", "generated by", "do not edit").
|
|
41
|
-
|
|
42
|
-
Returns:
|
|
43
|
-
True when the file header carries a generated-code marker.
|
|
44
|
-
|
|
45
|
-
"""
|
|
46
|
-
head = source.splitlines()[:_GENERATED_HEADER_LINES]
|
|
47
|
-
return any(_GENERATED_RE.search(line) for line in head)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def is_test_path(path: Path) -> bool:
|
|
51
|
-
"""Report whether `path` is a test file.
|
|
52
|
-
|
|
53
|
-
A test file is `conftest.py`, a `test_*.py` / `*_test.py` stem, or any file
|
|
54
|
-
under a `tests` / `test` directory segment.
|
|
55
|
-
|
|
56
|
-
Returns:
|
|
57
|
-
True when `path` belongs to the test tree.
|
|
58
|
-
|
|
59
|
-
"""
|
|
60
|
-
name = path.name
|
|
61
|
-
if name == "conftest.py" or name.startswith("test_") or name.endswith("_test.py"):
|
|
62
|
-
return True
|
|
63
|
-
return any(part in _TEST_DIR_NAMES for part in path.parts)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/_ast_index.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/_first_party.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/mock_without_spec.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_fstring_in_log.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_secret_in_log.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/no_select_star.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/over_mocked_test.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_class_row.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_or_pattern.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/prefer_str_enum.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.30.0 → sarj_python_lint-0.31.0}/src/sarj_python_lint/rules/unused_mock_setup.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|