sarj-python-lint 0.44.0__tar.gz → 0.46.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.44.0 → sarj_python_lint-0.46.0}/PKG-INFO +26 -2
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/README.md +25 -1
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/pyproject.toml +1 -1
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/__main__.py +1 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/_comments.py +2 -1
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/_prose_budget.py +17 -2
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/_registry.py +4 -0
- sarj_python_lint-0.46.0/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +713 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/.gitignore +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/_docstrings.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/_fastapi.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/conditional_assertion_in_test.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/duplicate_test_body.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/duplicated_override_docstring.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/fastapi_openapi_contract.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/fixture_returns_bare_tuple.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/inefficient_string_concat_in_loop.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/interaction_only_test.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/kwarg_heavy_construction_in_test.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/kwonly_same_type_params.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_aggregation_in_store_query.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_fat_try_blocks.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_file_level_escape_hatch_noqa.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_file_level_suppression.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_fstring_in_log.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_gen_random_uuid_in_sql.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_isinstance_union_chain.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_long_comment.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_optional_tenant_predicate.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_query_with_many_joins.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_raw_sql_in_tests.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_sentinel_return_on_except.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_sequential_await.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_sleep_in_test_body.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_stdlib_logging.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/no_unreachable_after_terminal.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/parametrize_case_needs_id.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/prefer_real_store_in_tests.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/prefer_single_sentence_comment.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/prefer_timedelta_for_durations.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/pydantic_at_boundaries.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/single_public_export.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/sleep_with_computed_arg_in_test.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/tautological_mock_assertion.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/test_loops_over_literal_cases.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/trivially_true_assertion.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/xfail_requires_strict.py +0 -0
- {sarj_python_lint-0.44.0 → sarj_python_lint-0.46.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.46.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
|
|
@@ -28,7 +28,7 @@ uv tool install sarj-python-lint
|
|
|
28
28
|
|
|
29
29
|
```yaml
|
|
30
30
|
- repo: https://github.com/sarj-ai/standards
|
|
31
|
-
rev: python-v0.
|
|
31
|
+
rev: python-v0.46.0
|
|
32
32
|
hooks:
|
|
33
33
|
- id: sarj-no-sequential-await
|
|
34
34
|
- id: sarj-inefficient-string-concat-in-loop
|
|
@@ -36,6 +36,7 @@ uv tool install sarj-python-lint
|
|
|
36
36
|
- id: sarj-no-fat-try-blocks
|
|
37
37
|
- id: sarj-pydantic-at-boundaries
|
|
38
38
|
- id: sarj-fastapi-openapi-contract # SARJ094
|
|
39
|
+
- id: sarj-no-hidden-constructor-fallback # SARJ095 (warning)
|
|
39
40
|
- id: sarj-prefer-class-row
|
|
40
41
|
- id: sarj-prefer-timedelta-for-durations
|
|
41
42
|
- id: sarj-prefer-struct-over-namedtuple
|
|
@@ -61,6 +62,29 @@ function-local framework imports, imported router instances and the assembled
|
|
|
61
62
|
Existing projects can adopt the default-enabled rule with `--update-baseline`
|
|
62
63
|
and then shrink that baseline as endpoint contracts are repaired.
|
|
63
64
|
|
|
65
|
+
### Hidden constructor settings fallback (0.45.0)
|
|
66
|
+
|
|
67
|
+
`SARJ095` warns when a keyword-only constructor parameter defaults to `None`
|
|
68
|
+
and the constructor silently replaces it with a proven pydantic-settings value.
|
|
69
|
+
The effective dependency is invisible at the call site, and `value or
|
|
70
|
+
settings.VALUE` also treats an explicit falsey value as omitted. Make the
|
|
71
|
+
argument required and resolve the setting at the application composition root;
|
|
72
|
+
the annotation may remain nullable when `None` is still a valid explicit value.
|
|
73
|
+
|
|
74
|
+
The rule resolves same-module settings objects, imports, aliases and re-exports
|
|
75
|
+
back to an instance of a `pydantic_settings.BaseSettings` subclass. Literal and
|
|
76
|
+
enum defaults, mutable-container initialization, arbitrary factories and
|
|
77
|
+
clients, module constants, environment-variable APIs, other parameters and
|
|
78
|
+
instance state are deliberately outside v1. No autofix is offered because
|
|
79
|
+
changing constructor optionality requires coordinated call-site edits.
|
|
80
|
+
|
|
81
|
+
Measured over 4,638 tracked Python files in 33 first-party repositories: three
|
|
82
|
+
constructor warnings across two repositories, all three actionable. A pinned
|
|
83
|
+
15-repository OSS sweep covered 29,203 files and produced zero reports, which is
|
|
84
|
+
compatibility evidence rather than a precision claim. An environment-variable
|
|
85
|
+
arm was rejected before shipping: it reported a public first-party library and
|
|
86
|
+
two intentional LiteLLM integration constructors, all non-actionable.
|
|
87
|
+
|
|
64
88
|
### Test-quality rules (0.15.0)
|
|
65
89
|
|
|
66
90
|
Mined from an AST audit of ~7,500 test functions across two production repos.
|
|
@@ -10,7 +10,7 @@ uv tool install sarj-python-lint
|
|
|
10
10
|
|
|
11
11
|
```yaml
|
|
12
12
|
- repo: https://github.com/sarj-ai/standards
|
|
13
|
-
rev: python-v0.
|
|
13
|
+
rev: python-v0.46.0
|
|
14
14
|
hooks:
|
|
15
15
|
- id: sarj-no-sequential-await
|
|
16
16
|
- id: sarj-inefficient-string-concat-in-loop
|
|
@@ -18,6 +18,7 @@ uv tool install sarj-python-lint
|
|
|
18
18
|
- id: sarj-no-fat-try-blocks
|
|
19
19
|
- id: sarj-pydantic-at-boundaries
|
|
20
20
|
- id: sarj-fastapi-openapi-contract # SARJ094
|
|
21
|
+
- id: sarj-no-hidden-constructor-fallback # SARJ095 (warning)
|
|
21
22
|
- id: sarj-prefer-class-row
|
|
22
23
|
- id: sarj-prefer-timedelta-for-durations
|
|
23
24
|
- id: sarj-prefer-struct-over-namedtuple
|
|
@@ -43,6 +44,29 @@ function-local framework imports, imported router instances and the assembled
|
|
|
43
44
|
Existing projects can adopt the default-enabled rule with `--update-baseline`
|
|
44
45
|
and then shrink that baseline as endpoint contracts are repaired.
|
|
45
46
|
|
|
47
|
+
### Hidden constructor settings fallback (0.45.0)
|
|
48
|
+
|
|
49
|
+
`SARJ095` warns when a keyword-only constructor parameter defaults to `None`
|
|
50
|
+
and the constructor silently replaces it with a proven pydantic-settings value.
|
|
51
|
+
The effective dependency is invisible at the call site, and `value or
|
|
52
|
+
settings.VALUE` also treats an explicit falsey value as omitted. Make the
|
|
53
|
+
argument required and resolve the setting at the application composition root;
|
|
54
|
+
the annotation may remain nullable when `None` is still a valid explicit value.
|
|
55
|
+
|
|
56
|
+
The rule resolves same-module settings objects, imports, aliases and re-exports
|
|
57
|
+
back to an instance of a `pydantic_settings.BaseSettings` subclass. Literal and
|
|
58
|
+
enum defaults, mutable-container initialization, arbitrary factories and
|
|
59
|
+
clients, module constants, environment-variable APIs, other parameters and
|
|
60
|
+
instance state are deliberately outside v1. No autofix is offered because
|
|
61
|
+
changing constructor optionality requires coordinated call-site edits.
|
|
62
|
+
|
|
63
|
+
Measured over 4,638 tracked Python files in 33 first-party repositories: three
|
|
64
|
+
constructor warnings across two repositories, all three actionable. A pinned
|
|
65
|
+
15-repository OSS sweep covered 29,203 files and produced zero reports, which is
|
|
66
|
+
compatibility evidence rather than a precision claim. An environment-variable
|
|
67
|
+
arm was rejected before shipping: it reported a public first-party library and
|
|
68
|
+
two intentional LiteLLM integration constructors, all non-actionable.
|
|
69
|
+
|
|
46
70
|
### Test-quality rules (0.15.0)
|
|
47
71
|
|
|
48
72
|
Mined from an AST audit of ~7,500 test functions across two production repos.
|
|
@@ -9,6 +9,7 @@ import tokenize
|
|
|
9
9
|
from typing import TYPE_CHECKING, NamedTuple
|
|
10
10
|
|
|
11
11
|
from sarj_python_lint.rule_base import parse_or_none
|
|
12
|
+
from sarj_python_lint.rules._ast_index import nodes
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
if TYPE_CHECKING:
|
|
@@ -498,7 +499,7 @@ def statement_comment_walls(
|
|
|
498
499
|
return {}
|
|
499
500
|
comments = {line: (col, body) for line, col, body in standalone}
|
|
500
501
|
walls: dict[int, frozenset[int]] = {}
|
|
501
|
-
for owner in ast.
|
|
502
|
+
for owner in nodes(tree, ast.AST):
|
|
502
503
|
for value in _owned_statement_lists(owner):
|
|
503
504
|
statements = [item for item in value if _is_wall_statement(item)]
|
|
504
505
|
if len(statements) < _WALL_MIN_STATEMENTS:
|
{sarj_python_lint-0.44.0 → sarj_python_lint-0.46.0}/src/sarj_python_lint/rules/_prose_budget.py
RENAMED
|
@@ -10,6 +10,7 @@ import tokenize
|
|
|
10
10
|
from typing import TYPE_CHECKING, Final
|
|
11
11
|
|
|
12
12
|
from sarj_python_lint.rule_base import parse_or_none
|
|
13
|
+
from sarj_python_lint.rules._ast_index import nodes
|
|
13
14
|
from sarj_python_lint.rules._docstrings import PROMPT_DECORATOR_MARKERS, decorator_markers, sections
|
|
14
15
|
from sarj_python_lint.rules._paths import is_generated
|
|
15
16
|
|
|
@@ -53,6 +54,9 @@ class ProseGroup:
|
|
|
53
54
|
typed_sections: frozenset[str] = frozenset()
|
|
54
55
|
|
|
55
56
|
|
|
57
|
+
_last_groups: tuple[str, str, tuple[ProseGroup, ...]] | None = None
|
|
58
|
+
|
|
59
|
+
|
|
56
60
|
def sentence_units(text: str) -> int:
|
|
57
61
|
"""Count prose sentences and unpunctuated list items deterministically."""
|
|
58
62
|
text = _without_examples_metadata(text)
|
|
@@ -98,6 +102,17 @@ def _without_examples_metadata(text: str) -> str:
|
|
|
98
102
|
|
|
99
103
|
def groups(path: Path, source: str) -> list[ProseGroup]:
|
|
100
104
|
"""Extract docstrings and contiguous own-line comment runs from one file."""
|
|
105
|
+
global _last_groups # ruff: ignore[global-statement] -- rules run sequentially per file.
|
|
106
|
+
path_key = str(path)
|
|
107
|
+
if _last_groups is not None and _last_groups[0] == path_key and _last_groups[1] is source:
|
|
108
|
+
return list(_last_groups[2])
|
|
109
|
+
extracted = _extract_groups(path, source)
|
|
110
|
+
_last_groups = (path_key, source, tuple(extracted))
|
|
111
|
+
return extracted
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _extract_groups(path: Path, source: str) -> list[ProseGroup]:
|
|
115
|
+
"""Perform the shared parse and tokenization once for adjacent prose rules."""
|
|
101
116
|
if is_generated(path, source):
|
|
102
117
|
return []
|
|
103
118
|
tree = parse_or_none(path, source)
|
|
@@ -140,8 +155,8 @@ def _comment_run(run: list[tokenize.TokenInfo]) -> ProseGroup:
|
|
|
140
155
|
|
|
141
156
|
def _docstring_groups(tree: ast.Module) -> list[ProseGroup]:
|
|
142
157
|
out: list[ProseGroup] = []
|
|
143
|
-
for node in ast.
|
|
144
|
-
if not
|
|
158
|
+
for node in nodes(tree, ast.Module, ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef):
|
|
159
|
+
if not node.body:
|
|
145
160
|
continue
|
|
146
161
|
first = node.body[0]
|
|
147
162
|
if not (
|
|
@@ -37,6 +37,9 @@ from sarj_python_lint.rules.no_first_party_private_import import (
|
|
|
37
37
|
)
|
|
38
38
|
from sarj_python_lint.rules.no_fstring_in_log import NoFstringInLog
|
|
39
39
|
from sarj_python_lint.rules.no_gen_random_uuid_in_sql import NoGenRandomUuidInSql
|
|
40
|
+
from sarj_python_lint.rules.no_hidden_constructor_fallback import (
|
|
41
|
+
NoHiddenConstructorFallback,
|
|
42
|
+
)
|
|
40
43
|
from sarj_python_lint.rules.no_isinstance_union_chain import NoIsinstanceUnionChain
|
|
41
44
|
from sarj_python_lint.rules.no_long_comment import NoLongComment
|
|
42
45
|
from sarj_python_lint.rules.no_offset_pagination import NoOffsetPagination
|
|
@@ -167,6 +170,7 @@ REGISTRY: dict[str, type[Rule]] = {
|
|
|
167
170
|
TrailingValueNarration.id: TrailingValueNarration,
|
|
168
171
|
NoStdlibLogging.id: NoStdlibLogging,
|
|
169
172
|
NoGenRandomUuidInSql.id: NoGenRandomUuidInSql,
|
|
173
|
+
NoHiddenConstructorFallback.id: NoHiddenConstructorFallback,
|
|
170
174
|
NoFileLevelEscapeHatchNoqa.id: NoFileLevelEscapeHatchNoqa,
|
|
171
175
|
NoOptionalTenantPredicate.id: NoOptionalTenantPredicate,
|
|
172
176
|
NoTautologicalExpect.id: NoTautologicalExpect,
|