sarj-python-lint 0.84.2__tar.gz → 0.84.3__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.84.2 → sarj_python_lint-0.84.3}/PKG-INFO +1 -1
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/pyproject.toml +1 -1
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/_registry.py +4 -2
- sarj_python_lint-0.84.3/src/sarj_python_lint/rules/no_delete_statement.py +104 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -1
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +5 -5
- sarj_python_lint-0.84.3/src/sarj_python_lint/rules/no_unused_value_marker.py +141 -0
- sarj_python_lint-0.84.2/src/sarj_python_lint/rules/no_deleted_only_override_parameter.py +0 -257
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/.gitignore +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/LICENSE +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/README.md +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/_analysis_session.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/_filesystem.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/_python_target.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/_comments.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/_docstrings.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/_fastapi.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/_imports.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/_project_index.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_vague_annotations.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/pydantic_at_boundaries.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/require_typed_http_test_response.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
- {sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/unused_test_factory_option.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sarj-python-lint
|
|
3
|
-
Version: 0.84.
|
|
3
|
+
Version: 0.84.3
|
|
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/
|
|
@@ -34,7 +34,7 @@ from sarj_python_lint.rules.no_copied_inherited_docstring import NoCopiedInherit
|
|
|
34
34
|
from sarj_python_lint.rules.no_cors_wildcard_with_credentials import (
|
|
35
35
|
NoCorsWildcardWithCredentials,
|
|
36
36
|
)
|
|
37
|
-
from sarj_python_lint.rules.
|
|
37
|
+
from sarj_python_lint.rules.no_delete_statement import NoDeleteStatement
|
|
38
38
|
from sarj_python_lint.rules.no_dunder_all import NoDunderAll
|
|
39
39
|
from sarj_python_lint.rules.no_duplicate_dunder_all_entry import NoDuplicateDunderAllEntry
|
|
40
40
|
from sarj_python_lint.rules.no_excessive_cognitive_complexity import NoExcessiveCognitiveComplexity
|
|
@@ -77,6 +77,7 @@ from sarj_python_lint.rules.no_unique_violation_message_match import (
|
|
|
77
77
|
NoUniqueViolationMessageMatch,
|
|
78
78
|
)
|
|
79
79
|
from sarj_python_lint.rules.no_unnecessary_docstring import NoUnnecessaryDocstring
|
|
80
|
+
from sarj_python_lint.rules.no_unused_value_marker import NoUnusedValueMarker
|
|
80
81
|
from sarj_python_lint.rules.no_vague_annotations import NoVagueAnnotations
|
|
81
82
|
from sarj_python_lint.rules.no_vague_suppression_description import (
|
|
82
83
|
NoVagueSuppressionDescription,
|
|
@@ -252,7 +253,7 @@ REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
|
|
|
252
253
|
PreferSelfDocumentingConstant.id: PreferSelfDocumentingConstant,
|
|
253
254
|
NoDuplicateDunderAllEntry.id: NoDuplicateDunderAllEntry,
|
|
254
255
|
NoDunderAll.id: NoDunderAll,
|
|
255
|
-
|
|
256
|
+
NoDeleteStatement.id: NoDeleteStatement,
|
|
256
257
|
NoCopiedInheritedDocstring.id: NoCopiedInheritedDocstring,
|
|
257
258
|
RedundantClassDocstring.id: RedundantClassDocstring,
|
|
258
259
|
RedundantModuleDocstring.id: RedundantModuleDocstring,
|
|
@@ -264,6 +265,7 @@ REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
|
|
|
264
265
|
NoRedundantLiteralDescription.id: NoRedundantLiteralDescription,
|
|
265
266
|
NoTypedDocSections.id: NoTypedDocSections,
|
|
266
267
|
NoUnnecessaryDocstring.id: NoUnnecessaryDocstring,
|
|
268
|
+
NoUnusedValueMarker.id: NoUnusedValueMarker,
|
|
267
269
|
PreferNominalIdTypes.id: PreferNominalIdTypes,
|
|
268
270
|
NoVagueAnnotations.id: NoVagueAnnotations,
|
|
269
271
|
NoUniqueViolationMessageMatch.id: NoUniqueViolationMessageMatch,
|
|
@@ -0,0 +1,104 @@
|
|
|
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
|
+
@final
|
|
28
|
+
class NoDeleteStatement(Rule):
|
|
29
|
+
id = "no-delete-statement"
|
|
30
|
+
code = "SARJ442"
|
|
31
|
+
documentation: ClassVar[RuleDocumentation | None] = RuleDocumentation(
|
|
32
|
+
summary="Avoid `del` statements; prefer constructing an immutable replacement value.",
|
|
33
|
+
rationale=(
|
|
34
|
+
"Deleting a name, attribute, or collection entry introduces lifetime or in-place mutation semantics "
|
|
35
|
+
"that are harder to follow than explicit value construction."
|
|
36
|
+
),
|
|
37
|
+
remediation=(
|
|
38
|
+
"Remove unused-value deletion markers. Rebuild collections with comprehensions, copies, or immutable "
|
|
39
|
+
"value helpers such as dataclasses.replace. When an external API or deliberate lifetime boundary "
|
|
40
|
+
"requires deletion, add an exact SARJ442 suppression on the statement and explain why."
|
|
41
|
+
),
|
|
42
|
+
category=RuleCategory.MAINTAINABILITY,
|
|
43
|
+
autofix=AutofixPolicy.NONE,
|
|
44
|
+
aliases=("no-deleted-only-override-parameter",),
|
|
45
|
+
limitations=(
|
|
46
|
+
"Every authored Python del statement is reported once, regardless of how many targets it contains.",
|
|
47
|
+
"The rule does not automatically rewrite deletion because the correct immutable replacement depends on ownership and value semantics.",
|
|
48
|
+
"Generated and vendored files are excluded.",
|
|
49
|
+
),
|
|
50
|
+
examples=(
|
|
51
|
+
RuleExample(
|
|
52
|
+
example_id="delete-mapping-entry",
|
|
53
|
+
title="Build a mapping without the removed entry",
|
|
54
|
+
outcome=ExampleOutcome.MATCH,
|
|
55
|
+
files=(
|
|
56
|
+
ExampleFile.python(
|
|
57
|
+
"app/request.py",
|
|
58
|
+
"payload = dict(raw_payload)\ndel payload['secret']\n",
|
|
59
|
+
),
|
|
60
|
+
),
|
|
61
|
+
focus_path=PurePosixPath("app/request.py"),
|
|
62
|
+
expected_count=1,
|
|
63
|
+
public=True,
|
|
64
|
+
),
|
|
65
|
+
RuleExample(
|
|
66
|
+
example_id="immutable-mapping-filter",
|
|
67
|
+
title="Construct the desired mapping directly",
|
|
68
|
+
outcome=ExampleOutcome.NO_MATCH,
|
|
69
|
+
files=(
|
|
70
|
+
ExampleFile.python(
|
|
71
|
+
"app/request.py",
|
|
72
|
+
"payload = {key: value for key, value in raw_payload.items() if key != 'secret'}\n",
|
|
73
|
+
),
|
|
74
|
+
),
|
|
75
|
+
focus_path=PurePosixPath("app/request.py"),
|
|
76
|
+
expected_count=0,
|
|
77
|
+
public=True,
|
|
78
|
+
),
|
|
79
|
+
),
|
|
80
|
+
)
|
|
81
|
+
description = documentation.summary
|
|
82
|
+
|
|
83
|
+
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
84
|
+
if "del" not in source or is_generated(path, source):
|
|
85
|
+
return []
|
|
86
|
+
tree = parse_or_none(path, source)
|
|
87
|
+
if tree is None:
|
|
88
|
+
return []
|
|
89
|
+
source_lines = source.splitlines()
|
|
90
|
+
return [
|
|
91
|
+
Diagnostic(
|
|
92
|
+
path=path,
|
|
93
|
+
line=node.lineno,
|
|
94
|
+
col=node.col_offset + 1,
|
|
95
|
+
code=self.code,
|
|
96
|
+
severity=Severity.WARNING,
|
|
97
|
+
message=(
|
|
98
|
+
"`del` introduces mutation or hidden lifetime behavior — construct the desired value "
|
|
99
|
+
"immutably, or add an exact SARJ442 suppression explaining why deletion is required."
|
|
100
|
+
),
|
|
101
|
+
)
|
|
102
|
+
for node in ast.walk(tree)
|
|
103
|
+
if isinstance(node, ast.Delete) and not is_suppressed(source_lines, node.lineno, self.code)
|
|
104
|
+
]
|
|
@@ -404,7 +404,7 @@ def _function_scopes(tree: ast.Module) -> list[_FunctionScope]:
|
|
|
404
404
|
|
|
405
405
|
@override
|
|
406
406
|
def visit_Lambda(self, node: ast.Lambda) -> None:
|
|
407
|
-
|
|
407
|
+
return
|
|
408
408
|
|
|
409
409
|
Collector().visit(tree)
|
|
410
410
|
return scopes
|
|
@@ -623,19 +623,19 @@ class _FunctionAnalyzer:
|
|
|
623
623
|
|
|
624
624
|
@override
|
|
625
625
|
def visit_Lambda(self, node: ast.Lambda) -> None:
|
|
626
|
-
|
|
626
|
+
return
|
|
627
627
|
|
|
628
628
|
@override
|
|
629
629
|
def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
|
|
630
|
-
|
|
630
|
+
return
|
|
631
631
|
|
|
632
632
|
@override
|
|
633
633
|
def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None:
|
|
634
|
-
|
|
634
|
+
return
|
|
635
635
|
|
|
636
636
|
@override
|
|
637
637
|
def visit_ClassDef(self, node: ast.ClassDef) -> None:
|
|
638
|
-
|
|
638
|
+
return
|
|
639
639
|
|
|
640
640
|
Visitor().visit(node)
|
|
641
641
|
|
|
@@ -0,0 +1,141 @@
|
|
|
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
|
+
@final
|
|
28
|
+
class NoUnusedValueMarker(Rule):
|
|
29
|
+
id = "no-unused-value-marker"
|
|
30
|
+
code = "SARJ452"
|
|
31
|
+
documentation: ClassVar[RuleDocumentation | None] = RuleDocumentation(
|
|
32
|
+
summary="Do not use standalone assignments to `_` to discard values.",
|
|
33
|
+
rationale=(
|
|
34
|
+
"A standalone `_ = value` assignment adds a binding without clarifying intent. A side-effecting call can "
|
|
35
|
+
"be invoked directly, while an unused parameter should be addressed at its declaration."
|
|
36
|
+
),
|
|
37
|
+
remediation=(
|
|
38
|
+
"Invoke calls directly instead of assigning their result to `_`. Remove or rename locally owned unused "
|
|
39
|
+
"values; when an external interface requires the exact unused parameter, put a narrow reasoned linter "
|
|
40
|
+
"suppression on its declaration."
|
|
41
|
+
),
|
|
42
|
+
category=RuleCategory.MAINTAINABILITY,
|
|
43
|
+
autofix=AutofixPolicy.NONE,
|
|
44
|
+
limitations=(
|
|
45
|
+
"Plain and annotated assignments whose standalone target is `_` are reported, including chained assignments.",
|
|
46
|
+
"Unpacking placeholders, loop targets, wildcard imports, and underscore-prefixed names are excluded.",
|
|
47
|
+
"Generated and vendored files are excluded.",
|
|
48
|
+
),
|
|
49
|
+
examples=(
|
|
50
|
+
RuleExample(
|
|
51
|
+
example_id="unused-parameter-marker",
|
|
52
|
+
title="Do not manufacture a use for interface parameters",
|
|
53
|
+
outcome=ExampleOutcome.MATCH,
|
|
54
|
+
files=(
|
|
55
|
+
ExampleFile.python(
|
|
56
|
+
"app/adapter.py",
|
|
57
|
+
"def render(root: Path, manifest: Path) -> str:\n _ = root, manifest\n return 'ready'\n",
|
|
58
|
+
),
|
|
59
|
+
),
|
|
60
|
+
focus_path=PurePosixPath("app/adapter.py"),
|
|
61
|
+
expected_count=1,
|
|
62
|
+
public=True,
|
|
63
|
+
),
|
|
64
|
+
RuleExample(
|
|
65
|
+
example_id="discarded-call-result",
|
|
66
|
+
title="Invoke a call directly when its result is intentionally unused",
|
|
67
|
+
outcome=ExampleOutcome.MATCH,
|
|
68
|
+
files=(
|
|
69
|
+
ExampleFile.python(
|
|
70
|
+
"app/adapter.py",
|
|
71
|
+
"_ = refresh_cache()\n",
|
|
72
|
+
),
|
|
73
|
+
),
|
|
74
|
+
focus_path=PurePosixPath("app/adapter.py"),
|
|
75
|
+
expected_count=1,
|
|
76
|
+
public=True,
|
|
77
|
+
scenario="call-result",
|
|
78
|
+
),
|
|
79
|
+
RuleExample(
|
|
80
|
+
example_id="direct-call",
|
|
81
|
+
title="Run the call without creating a discard binding",
|
|
82
|
+
outcome=ExampleOutcome.NO_MATCH,
|
|
83
|
+
files=(
|
|
84
|
+
ExampleFile.python(
|
|
85
|
+
"app/adapter.py",
|
|
86
|
+
"refresh_cache()\n",
|
|
87
|
+
),
|
|
88
|
+
),
|
|
89
|
+
focus_path=PurePosixPath("app/adapter.py"),
|
|
90
|
+
expected_count=0,
|
|
91
|
+
public=True,
|
|
92
|
+
scenario="call-result",
|
|
93
|
+
),
|
|
94
|
+
RuleExample(
|
|
95
|
+
example_id="unpacking-placeholder",
|
|
96
|
+
title="Keep a conventional placeholder inside unpacking",
|
|
97
|
+
outcome=ExampleOutcome.NO_MATCH,
|
|
98
|
+
files=(
|
|
99
|
+
ExampleFile.python(
|
|
100
|
+
"app/adapter.py",
|
|
101
|
+
"name, _ = load_pair()\n",
|
|
102
|
+
),
|
|
103
|
+
),
|
|
104
|
+
focus_path=PurePosixPath("app/adapter.py"),
|
|
105
|
+
expected_count=0,
|
|
106
|
+
public=True,
|
|
107
|
+
),
|
|
108
|
+
),
|
|
109
|
+
)
|
|
110
|
+
description = documentation.summary
|
|
111
|
+
|
|
112
|
+
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
113
|
+
if "_" not in source or "=" not in source or is_generated(path, source):
|
|
114
|
+
return []
|
|
115
|
+
tree = parse_or_none(path, source)
|
|
116
|
+
if tree is None:
|
|
117
|
+
return []
|
|
118
|
+
source_lines = source.splitlines()
|
|
119
|
+
return [
|
|
120
|
+
Diagnostic(
|
|
121
|
+
path=path,
|
|
122
|
+
line=node.lineno,
|
|
123
|
+
col=node.col_offset + 1,
|
|
124
|
+
code=self.code,
|
|
125
|
+
severity=Severity.WARNING,
|
|
126
|
+
message=(
|
|
127
|
+
"standalone assignment to `_` discards a value through an unnecessary binding — invoke calls "
|
|
128
|
+
"directly or address unused values at their declaration."
|
|
129
|
+
),
|
|
130
|
+
)
|
|
131
|
+
for node in ast.walk(tree)
|
|
132
|
+
if isinstance(node, (ast.Assign, ast.AnnAssign))
|
|
133
|
+
and _is_unused_value_marker(node)
|
|
134
|
+
and not is_suppressed(source_lines, node.lineno, self.code)
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _is_unused_value_marker(node: ast.Assign | ast.AnnAssign) -> bool:
|
|
139
|
+
if isinstance(node, ast.AnnAssign):
|
|
140
|
+
return node.value is not None and isinstance(node.target, ast.Name) and node.target.id == "_"
|
|
141
|
+
return any(isinstance(target, ast.Name) and target.id == "_" for target in node.targets)
|
|
@@ -1,257 +0,0 @@
|
|
|
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
|
-
_collect_override_aliases(statement, direct, conflicting)
|
|
128
|
-
continue
|
|
129
|
-
if isinstance(statement, ast.Import):
|
|
130
|
-
for alias in statement.names:
|
|
131
|
-
name = alias.asname or alias.name.split(".")[0]
|
|
132
|
-
if alias.name in _OVERRIDE_MODULES and name not in modules:
|
|
133
|
-
modules.add(name)
|
|
134
|
-
else:
|
|
135
|
-
conflicting.add(name)
|
|
136
|
-
continue
|
|
137
|
-
conflicting.update(_statement_bound_names(statement))
|
|
138
|
-
return frozenset(direct - conflicting), frozenset(modules - conflicting)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def _statement_bound_names(statement: ast.stmt) -> set[str]:
|
|
142
|
-
names: set[str] = set()
|
|
143
|
-
if isinstance(statement, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
|
|
144
|
-
names.add(statement.name)
|
|
145
|
-
for node in ast.walk(statement):
|
|
146
|
-
if isinstance(node, ast.Name) and isinstance(node.ctx, ast.Store):
|
|
147
|
-
names.add(node.id)
|
|
148
|
-
elif isinstance(node, ast.alias):
|
|
149
|
-
names.add(node.asname or node.name.split(".")[0])
|
|
150
|
-
return names
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
def _class_bound_names(node: ast.ClassDef) -> frozenset[str]:
|
|
154
|
-
names: set[str] = set()
|
|
155
|
-
for statement in node.body:
|
|
156
|
-
if isinstance(statement, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
157
|
-
names.add(statement.name)
|
|
158
|
-
elif not isinstance(statement, ast.ClassDef):
|
|
159
|
-
names.update(_statement_bound_names(statement))
|
|
160
|
-
return frozenset(names)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
def _is_override(
|
|
164
|
-
method: ast.FunctionDef | ast.AsyncFunctionDef,
|
|
165
|
-
direct_aliases: frozenset[str],
|
|
166
|
-
module_aliases: frozenset[str],
|
|
167
|
-
) -> bool:
|
|
168
|
-
for decorator in method.decorator_list:
|
|
169
|
-
if isinstance(decorator, ast.Name) and decorator.id in direct_aliases:
|
|
170
|
-
return True
|
|
171
|
-
if (
|
|
172
|
-
isinstance(decorator, ast.Attribute)
|
|
173
|
-
and decorator.attr == "override"
|
|
174
|
-
and isinstance(decorator.value, ast.Name)
|
|
175
|
-
and decorator.value.id in module_aliases
|
|
176
|
-
):
|
|
177
|
-
return True
|
|
178
|
-
return False
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
def _method_diagnostics(
|
|
182
|
-
path: Path,
|
|
183
|
-
source_lines: list[str],
|
|
184
|
-
method: ast.FunctionDef | ast.AsyncFunctionDef,
|
|
185
|
-
) -> list[Diagnostic]:
|
|
186
|
-
delete_targets = _direct_delete_targets(method.body)
|
|
187
|
-
parameters = {
|
|
188
|
-
argument.arg
|
|
189
|
-
for argument in (
|
|
190
|
-
*method.args.posonlyargs,
|
|
191
|
-
*method.args.args,
|
|
192
|
-
*method.args.kwonlyargs,
|
|
193
|
-
*(() if method.args.vararg is None else (method.args.vararg,)),
|
|
194
|
-
*(() if method.args.kwarg is None else (method.args.kwarg,)),
|
|
195
|
-
)
|
|
196
|
-
}
|
|
197
|
-
direct_nodes = {id(node) for nodes in delete_targets.values() for node in nodes}
|
|
198
|
-
other_occurrences = {
|
|
199
|
-
node.id
|
|
200
|
-
for statement in method.body
|
|
201
|
-
for node in ast.walk(statement)
|
|
202
|
-
if isinstance(node, ast.Name) and id(node) not in direct_nodes
|
|
203
|
-
}
|
|
204
|
-
diagnostics: list[Diagnostic] = []
|
|
205
|
-
for name in sorted(parameters.intersection(delete_targets) - other_occurrences):
|
|
206
|
-
target = min(delete_targets[name], key=lambda node: (node.lineno, node.col_offset))
|
|
207
|
-
if is_suppressed(source_lines, target.lineno, NoDeletedOnlyOverrideParameter.code):
|
|
208
|
-
continue
|
|
209
|
-
diagnostics.append(
|
|
210
|
-
Diagnostic(
|
|
211
|
-
path=path,
|
|
212
|
-
line=target.lineno,
|
|
213
|
-
col=target.col_offset + 1,
|
|
214
|
-
code=NoDeletedOnlyOverrideParameter.code,
|
|
215
|
-
severity=Severity.WARNING,
|
|
216
|
-
message=(
|
|
217
|
-
f"override parameter `{name}` is referenced only by direct deletion — remove the unused-marker "
|
|
218
|
-
"deletion, or suppress this warning with a reason when early reference release is intentional."
|
|
219
|
-
),
|
|
220
|
-
)
|
|
221
|
-
)
|
|
222
|
-
return diagnostics
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
def _direct_delete_targets(statements: list[ast.stmt]) -> dict[str, list[ast.Name]]:
|
|
226
|
-
targets: dict[str, list[ast.Name]] = {}
|
|
227
|
-
|
|
228
|
-
def visit(node: ast.AST) -> None:
|
|
229
|
-
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.Lambda, ast.ClassDef)):
|
|
230
|
-
return
|
|
231
|
-
if isinstance(node, ast.Delete):
|
|
232
|
-
for target in node.targets:
|
|
233
|
-
for name in _bare_delete_names(target):
|
|
234
|
-
targets.setdefault(name.id, []).append(name)
|
|
235
|
-
for child in ast.iter_child_nodes(node):
|
|
236
|
-
visit(child)
|
|
237
|
-
|
|
238
|
-
for statement in statements:
|
|
239
|
-
visit(statement)
|
|
240
|
-
return targets
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
def _bare_delete_names(target: ast.expr) -> list[ast.Name]:
|
|
244
|
-
if isinstance(target, ast.Name):
|
|
245
|
-
return [target]
|
|
246
|
-
if isinstance(target, (ast.Tuple, ast.List)):
|
|
247
|
-
return [name for item in target.elts for name in _bare_delete_names(item)]
|
|
248
|
-
return []
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
def _collect_override_aliases(statement: ast.ImportFrom, direct: set[str], conflicting: set[str]) -> None:
|
|
252
|
-
for alias in statement.names:
|
|
253
|
-
name = alias.asname or alias.name
|
|
254
|
-
if alias.name == "override" and name not in direct:
|
|
255
|
-
direct.add(name)
|
|
256
|
-
else:
|
|
257
|
-
conflicting.add(name)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/_analysis_session.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.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/_ast_index.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/_docstrings.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/_first_party.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/_fixed_record.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/_project_index.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/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.84.2 → sarj_python_lint-0.84.3}/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
|
{sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/mock_without_spec.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_comment_cruft.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_dunder_all.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.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/no_secret_in_log.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/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
|
{sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/over_mocked_test.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/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
|
|
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.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/prefer_or_pattern.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.84.2 → sarj_python_lint-0.84.3}/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
|
|
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.84.2 → sarj_python_lint-0.84.3}/src/sarj_python_lint/rules/unused_mock_setup.py
RENAMED
|
File without changes
|
|
File without changes
|