sarj-python-lint 0.81.12__tar.gz → 0.81.13__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.81.12 → sarj_python_lint-0.81.13}/PKG-INFO +1 -1
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/pyproject.toml +1 -1
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_registry.py +2 -0
- sarj_python_lint-0.81.13/src/sarj_python_lint/rules/no_deleted_only_override_parameter.py +253 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_dunder_all.py +1 -1
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +5 -5
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +1 -1
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +4 -4
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/over_mocked_test.py +1 -1
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_class_row.py +1 -1
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/.gitignore +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/LICENSE +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/README.md +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/_analysis_session.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/_filesystem.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/_python_target.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_comments.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_docstrings.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_fastapi.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_imports.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_project_index.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/pydantic_at_boundaries.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
- {sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sarj-python-lint
|
|
3
|
-
Version: 0.81.
|
|
3
|
+
Version: 0.81.13
|
|
4
4
|
Summary: Custom Python lint rules — AST-based, pre-commit-friendly, hypermodern defaults
|
|
5
5
|
Project-URL: Homepage, https://code-standards.sarj.ai/rules/python/
|
|
6
6
|
Project-URL: Documentation, https://code-standards.sarj.ai/rules/python/
|
{sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_registry.py
RENAMED
|
@@ -33,6 +33,7 @@ from sarj_python_lint.rules.no_copied_inherited_docstring import NoCopiedInherit
|
|
|
33
33
|
from sarj_python_lint.rules.no_cors_wildcard_with_credentials import (
|
|
34
34
|
NoCorsWildcardWithCredentials,
|
|
35
35
|
)
|
|
36
|
+
from sarj_python_lint.rules.no_deleted_only_override_parameter import NoDeletedOnlyOverrideParameter
|
|
36
37
|
from sarj_python_lint.rules.no_dunder_all import NoDunderAll
|
|
37
38
|
from sarj_python_lint.rules.no_duplicate_dunder_all_entry import NoDuplicateDunderAllEntry
|
|
38
39
|
from sarj_python_lint.rules.no_fastapi_on_event import NoFastapiOnEvent
|
|
@@ -232,6 +233,7 @@ REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
|
|
|
232
233
|
PreferSelfDocumentingConstant.id: PreferSelfDocumentingConstant,
|
|
233
234
|
NoDuplicateDunderAllEntry.id: NoDuplicateDunderAllEntry,
|
|
234
235
|
NoDunderAll.id: NoDunderAll,
|
|
236
|
+
NoDeletedOnlyOverrideParameter.id: NoDeletedOnlyOverrideParameter,
|
|
235
237
|
NoCopiedInheritedDocstring.id: NoCopiedInheritedDocstring,
|
|
236
238
|
RedundantClassDocstring.id: RedundantClassDocstring,
|
|
237
239
|
RedundantModuleDocstring.id: RedundantModuleDocstring,
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
from pathlib import PurePosixPath
|
|
5
|
+
from typing import TYPE_CHECKING, ClassVar, final
|
|
6
|
+
|
|
7
|
+
from sarj_python_lint.rule_base import (
|
|
8
|
+
AutofixPolicy,
|
|
9
|
+
Diagnostic,
|
|
10
|
+
ExampleFile,
|
|
11
|
+
ExampleOutcome,
|
|
12
|
+
Rule,
|
|
13
|
+
RuleCategory,
|
|
14
|
+
RuleDocumentation,
|
|
15
|
+
RuleExample,
|
|
16
|
+
Severity,
|
|
17
|
+
is_suppressed,
|
|
18
|
+
parse_or_none,
|
|
19
|
+
)
|
|
20
|
+
from sarj_python_lint.rules._paths import is_generated
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
_OVERRIDE_MODULES = frozenset({"typing", "typing_extensions"})
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@final
|
|
31
|
+
class NoDeletedOnlyOverrideParameter(Rule):
|
|
32
|
+
id = "no-deleted-only-override-parameter"
|
|
33
|
+
code = "SARJ442"
|
|
34
|
+
documentation: ClassVar[RuleDocumentation | None] = RuleDocumentation(
|
|
35
|
+
summary="Do not use direct deletion as the only reference to an override parameter.",
|
|
36
|
+
rationale=(
|
|
37
|
+
"Deleting an otherwise unread override parameter is commonly used as an unused-value marker, but "
|
|
38
|
+
"`@override` already preserves the interface contract without requiring a synthetic reference."
|
|
39
|
+
),
|
|
40
|
+
remediation=(
|
|
41
|
+
"Keep the override signature and remove the deletion marker. If deletion intentionally shortens the "
|
|
42
|
+
"reference lifetime, retain it with an exact suppression that explains the lifetime boundary."
|
|
43
|
+
),
|
|
44
|
+
category=RuleCategory.STYLE,
|
|
45
|
+
autofix=AutofixPolicy.NONE,
|
|
46
|
+
limitations=(
|
|
47
|
+
"Only direct methods of module-level classes decorated by a proven runtime import of typing.override or typing_extensions.override are checked.",
|
|
48
|
+
"A parameter is reported only when every occurrence in the method is a direct bare-name deletion; any load, rebinding, nested occurrence, attribute deletion, or subscription deletion excludes it.",
|
|
49
|
+
"Removing a deletion can change reference lifetime, finalization, frame tracing, or locals() visibility, so the rule has no autofix and supports an exact reasoned suppression.",
|
|
50
|
+
"Generated files are excluded.",
|
|
51
|
+
),
|
|
52
|
+
examples=(
|
|
53
|
+
RuleExample(
|
|
54
|
+
example_id="override-parameter-delete-marker",
|
|
55
|
+
title="Remove a deletion used only as an override parameter marker",
|
|
56
|
+
outcome=ExampleOutcome.MATCH,
|
|
57
|
+
files=(
|
|
58
|
+
ExampleFile.python(
|
|
59
|
+
"app/handler.py",
|
|
60
|
+
"from typing import override\n\n"
|
|
61
|
+
"class Handler(BaseHandler):\n"
|
|
62
|
+
" @override\n"
|
|
63
|
+
" def handle(self, request: Request) -> None:\n"
|
|
64
|
+
" del request\n"
|
|
65
|
+
" emit_ready()\n",
|
|
66
|
+
),
|
|
67
|
+
),
|
|
68
|
+
focus_path=PurePosixPath("app/handler.py"),
|
|
69
|
+
expected_count=1,
|
|
70
|
+
public=True,
|
|
71
|
+
),
|
|
72
|
+
RuleExample(
|
|
73
|
+
example_id="override-uses-parameter",
|
|
74
|
+
title="Keep an override parameter that contributes to behavior",
|
|
75
|
+
outcome=ExampleOutcome.NO_MATCH,
|
|
76
|
+
files=(
|
|
77
|
+
ExampleFile.python(
|
|
78
|
+
"app/handler.py",
|
|
79
|
+
"from typing import override\n\n"
|
|
80
|
+
"class Handler(BaseHandler):\n"
|
|
81
|
+
" @override\n"
|
|
82
|
+
" def handle(self, request: Request) -> None:\n"
|
|
83
|
+
" dispatch(request)\n",
|
|
84
|
+
),
|
|
85
|
+
),
|
|
86
|
+
focus_path=PurePosixPath("app/handler.py"),
|
|
87
|
+
expected_count=0,
|
|
88
|
+
public=True,
|
|
89
|
+
),
|
|
90
|
+
),
|
|
91
|
+
)
|
|
92
|
+
description = documentation.summary
|
|
93
|
+
|
|
94
|
+
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
95
|
+
if "del" not in source or "override" not in source or is_generated(path, source):
|
|
96
|
+
return []
|
|
97
|
+
tree = parse_or_none(path, source)
|
|
98
|
+
if tree is None:
|
|
99
|
+
return []
|
|
100
|
+
direct_aliases, module_aliases = _proven_override_imports(tree)
|
|
101
|
+
if not direct_aliases and not module_aliases:
|
|
102
|
+
return []
|
|
103
|
+
source_lines = source.splitlines()
|
|
104
|
+
diagnostics: list[Diagnostic] = []
|
|
105
|
+
for statement in tree.body:
|
|
106
|
+
if not isinstance(statement, ast.ClassDef):
|
|
107
|
+
continue
|
|
108
|
+
class_bound = _class_bound_names(statement)
|
|
109
|
+
available_direct = direct_aliases - class_bound
|
|
110
|
+
available_modules = module_aliases - class_bound
|
|
111
|
+
for method in statement.body:
|
|
112
|
+
if not isinstance(method, (ast.FunctionDef, ast.AsyncFunctionDef)) or not _is_override(
|
|
113
|
+
method, available_direct, available_modules
|
|
114
|
+
):
|
|
115
|
+
continue
|
|
116
|
+
diagnostics.extend(_method_diagnostics(path, source_lines, method))
|
|
117
|
+
diagnostics.sort(key=lambda item: (item.line, item.col))
|
|
118
|
+
return diagnostics
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _proven_override_imports(tree: ast.Module) -> tuple[frozenset[str], frozenset[str]]:
|
|
122
|
+
direct: set[str] = set()
|
|
123
|
+
modules: set[str] = set()
|
|
124
|
+
conflicting: set[str] = set()
|
|
125
|
+
for statement in tree.body:
|
|
126
|
+
if isinstance(statement, ast.ImportFrom) and statement.module in _OVERRIDE_MODULES:
|
|
127
|
+
for alias in statement.names:
|
|
128
|
+
name = alias.asname or alias.name
|
|
129
|
+
if alias.name == "override" and name not in direct:
|
|
130
|
+
direct.add(name)
|
|
131
|
+
else:
|
|
132
|
+
conflicting.add(name)
|
|
133
|
+
continue
|
|
134
|
+
if isinstance(statement, ast.Import):
|
|
135
|
+
for alias in statement.names:
|
|
136
|
+
name = alias.asname or alias.name.split(".")[0]
|
|
137
|
+
if alias.name in _OVERRIDE_MODULES and name not in modules:
|
|
138
|
+
modules.add(name)
|
|
139
|
+
else:
|
|
140
|
+
conflicting.add(name)
|
|
141
|
+
continue
|
|
142
|
+
conflicting.update(_statement_bound_names(statement))
|
|
143
|
+
return frozenset(direct - conflicting), frozenset(modules - conflicting)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _statement_bound_names(statement: ast.stmt) -> set[str]:
|
|
147
|
+
names: set[str] = set()
|
|
148
|
+
if isinstance(statement, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
|
|
149
|
+
names.add(statement.name)
|
|
150
|
+
for node in ast.walk(statement):
|
|
151
|
+
if isinstance(node, ast.Name) and isinstance(node.ctx, ast.Store):
|
|
152
|
+
names.add(node.id)
|
|
153
|
+
elif isinstance(node, ast.alias):
|
|
154
|
+
names.add(node.asname or node.name.split(".")[0])
|
|
155
|
+
return names
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _class_bound_names(node: ast.ClassDef) -> frozenset[str]:
|
|
159
|
+
names: set[str] = set()
|
|
160
|
+
for statement in node.body:
|
|
161
|
+
if isinstance(statement, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
162
|
+
names.add(statement.name)
|
|
163
|
+
elif not isinstance(statement, ast.ClassDef):
|
|
164
|
+
names.update(_statement_bound_names(statement))
|
|
165
|
+
return frozenset(names)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _is_override(
|
|
169
|
+
method: ast.FunctionDef | ast.AsyncFunctionDef,
|
|
170
|
+
direct_aliases: frozenset[str],
|
|
171
|
+
module_aliases: frozenset[str],
|
|
172
|
+
) -> bool:
|
|
173
|
+
for decorator in method.decorator_list:
|
|
174
|
+
if isinstance(decorator, ast.Name) and decorator.id in direct_aliases:
|
|
175
|
+
return True
|
|
176
|
+
if (
|
|
177
|
+
isinstance(decorator, ast.Attribute)
|
|
178
|
+
and decorator.attr == "override"
|
|
179
|
+
and isinstance(decorator.value, ast.Name)
|
|
180
|
+
and decorator.value.id in module_aliases
|
|
181
|
+
):
|
|
182
|
+
return True
|
|
183
|
+
return False
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _method_diagnostics(
|
|
187
|
+
path: Path,
|
|
188
|
+
source_lines: list[str],
|
|
189
|
+
method: ast.FunctionDef | ast.AsyncFunctionDef,
|
|
190
|
+
) -> list[Diagnostic]:
|
|
191
|
+
delete_targets = _direct_delete_targets(method.body)
|
|
192
|
+
parameters = {
|
|
193
|
+
argument.arg
|
|
194
|
+
for argument in (
|
|
195
|
+
*method.args.posonlyargs,
|
|
196
|
+
*method.args.args,
|
|
197
|
+
*method.args.kwonlyargs,
|
|
198
|
+
*(() if method.args.vararg is None else (method.args.vararg,)),
|
|
199
|
+
*(() if method.args.kwarg is None else (method.args.kwarg,)),
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
direct_nodes = {id(node) for nodes in delete_targets.values() for node in nodes}
|
|
203
|
+
other_occurrences = {
|
|
204
|
+
node.id
|
|
205
|
+
for statement in method.body
|
|
206
|
+
for node in ast.walk(statement)
|
|
207
|
+
if isinstance(node, ast.Name) and id(node) not in direct_nodes
|
|
208
|
+
}
|
|
209
|
+
diagnostics: list[Diagnostic] = []
|
|
210
|
+
for name in sorted(parameters.intersection(delete_targets) - other_occurrences):
|
|
211
|
+
target = min(delete_targets[name], key=lambda node: (node.lineno, node.col_offset))
|
|
212
|
+
if is_suppressed(source_lines, target.lineno, NoDeletedOnlyOverrideParameter.code):
|
|
213
|
+
continue
|
|
214
|
+
diagnostics.append(
|
|
215
|
+
Diagnostic(
|
|
216
|
+
path=path,
|
|
217
|
+
line=target.lineno,
|
|
218
|
+
col=target.col_offset + 1,
|
|
219
|
+
code=NoDeletedOnlyOverrideParameter.code,
|
|
220
|
+
severity=Severity.WARNING,
|
|
221
|
+
message=(
|
|
222
|
+
f"override parameter `{name}` is referenced only by direct deletion — remove the unused-marker "
|
|
223
|
+
"deletion, or suppress this warning with a reason when early reference release is intentional."
|
|
224
|
+
),
|
|
225
|
+
)
|
|
226
|
+
)
|
|
227
|
+
return diagnostics
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _direct_delete_targets(statements: list[ast.stmt]) -> dict[str, list[ast.Name]]:
|
|
231
|
+
targets: dict[str, list[ast.Name]] = {}
|
|
232
|
+
|
|
233
|
+
def visit(node: ast.AST) -> None:
|
|
234
|
+
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.Lambda, ast.ClassDef)):
|
|
235
|
+
return
|
|
236
|
+
if isinstance(node, ast.Delete):
|
|
237
|
+
for target in node.targets:
|
|
238
|
+
for name in _bare_delete_names(target):
|
|
239
|
+
targets.setdefault(name.id, []).append(name)
|
|
240
|
+
for child in ast.iter_child_nodes(node):
|
|
241
|
+
visit(child)
|
|
242
|
+
|
|
243
|
+
for statement in statements:
|
|
244
|
+
visit(statement)
|
|
245
|
+
return targets
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _bare_delete_names(target: ast.expr) -> list[ast.Name]:
|
|
249
|
+
if isinstance(target, ast.Name):
|
|
250
|
+
return [target]
|
|
251
|
+
if isinstance(target, (ast.Tuple, ast.List)):
|
|
252
|
+
return [name for item in target.elts for name in _bare_delete_names(item)]
|
|
253
|
+
return []
|
|
@@ -874,20 +874,20 @@ class _LocalBindingCollector(ast.NodeVisitor):
|
|
|
874
874
|
|
|
875
875
|
@override
|
|
876
876
|
def visit_Lambda(self, node: ast.Lambda) -> None:
|
|
877
|
-
|
|
877
|
+
return None
|
|
878
878
|
|
|
879
879
|
@override
|
|
880
880
|
def visit_ListComp(self, node: ast.ListComp) -> None:
|
|
881
|
-
|
|
881
|
+
return None
|
|
882
882
|
|
|
883
883
|
@override
|
|
884
884
|
def visit_SetComp(self, node: ast.SetComp) -> None:
|
|
885
|
-
|
|
885
|
+
return None
|
|
886
886
|
|
|
887
887
|
@override
|
|
888
888
|
def visit_DictComp(self, node: ast.DictComp) -> None:
|
|
889
|
-
|
|
889
|
+
return None
|
|
890
890
|
|
|
891
891
|
@override
|
|
892
892
|
def visit_GeneratorExp(self, node: ast.GeneratorExp) -> None:
|
|
893
|
-
|
|
893
|
+
return None
|
|
@@ -392,19 +392,19 @@ class FunctionAnalyzer(ast.NodeVisitor):
|
|
|
392
392
|
|
|
393
393
|
@override
|
|
394
394
|
def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
|
|
395
|
-
|
|
395
|
+
return None # A nested helper owns a separate scope and is intentionally not inferred.
|
|
396
396
|
|
|
397
397
|
@override
|
|
398
398
|
def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None:
|
|
399
|
-
|
|
399
|
+
return None
|
|
400
400
|
|
|
401
401
|
@override
|
|
402
402
|
def visit_ClassDef(self, node: ast.ClassDef) -> None:
|
|
403
|
-
|
|
403
|
+
return None
|
|
404
404
|
|
|
405
405
|
@override
|
|
406
406
|
def visit_Lambda(self, node: ast.Lambda) -> None:
|
|
407
|
-
|
|
407
|
+
return None
|
|
408
408
|
|
|
409
409
|
@override
|
|
410
410
|
def visit_Assign(self, node: ast.Assign) -> None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/_analysis_session.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/_python_target.py
RENAMED
|
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.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/__init__.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_ast_index.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_comments.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_docstrings.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_fastapi.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_first_party.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_fixed_record.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_imports.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_logging.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_project_index.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_prose_budget.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/_test_assertions.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
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_comment_cruft.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
|
|
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.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/no_secret_in_log.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.81.12 → sarj_python_lint-0.81.13}/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
|
|
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
|
|
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.81.12 → sarj_python_lint-0.81.13}/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
|
|
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.81.12 → sarj_python_lint-0.81.13}/src/sarj_python_lint/rules/stepdown.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
|