sarj-python-lint 0.81.0__tar.gz → 0.81.2__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.0 → sarj_python_lint-0.81.2}/PKG-INFO +1 -1
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/pyproject.toml +1 -1
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/_registry.py +2 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_dunder_all.py +2 -1
- sarj_python_lint-0.81.2/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +190 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +1 -2
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/.gitignore +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/LICENSE +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/README.md +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/_analysis_session.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/_filesystem.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/_python_target.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/_comments.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/_docstrings.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/_fastapi.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/_imports.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/_project_index.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/pydantic_at_boundaries.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
- {sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/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.2
|
|
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/
|
|
@@ -59,6 +59,7 @@ from sarj_python_lint.rules.no_random_uuid_in_sql import NoRandomUuidInSql
|
|
|
59
59
|
from sarj_python_lint.rules.no_raw_connection_in_tests import NoRawConnectionInTests
|
|
60
60
|
from sarj_python_lint.rules.no_raw_source_text_test_oracle import NoRawSourceTextTestOracle
|
|
61
61
|
from sarj_python_lint.rules.no_redundant_literal_description import NoRedundantLiteralDescription
|
|
62
|
+
from sarj_python_lint.rules.no_redundant_module_alias_exports import NoRedundantModuleAliasExports
|
|
62
63
|
from sarj_python_lint.rules.no_repeated_string_literal import NoRepeatedStringLiteral
|
|
63
64
|
from sarj_python_lint.rules.no_repeated_test_body import NoRepeatedTestBody
|
|
64
65
|
from sarj_python_lint.rules.no_restated_comment import NoRestatedComment
|
|
@@ -204,6 +205,7 @@ REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
|
|
|
204
205
|
RedundantDocstring.id: RedundantDocstring,
|
|
205
206
|
TrailingValueNarration.id: TrailingValueNarration,
|
|
206
207
|
NoRandomUuidInSql.id: NoRandomUuidInSql,
|
|
208
|
+
NoRedundantModuleAliasExports.id: NoRedundantModuleAliasExports,
|
|
207
209
|
NoHiddenConstructorFallback.id: NoHiddenConstructorFallback,
|
|
208
210
|
NoFileLevelEscapeHatchSuppression.id: NoFileLevelEscapeHatchSuppression,
|
|
209
211
|
NoFastapiOnEvent.id: NoFastapiOnEvent,
|
{sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_dunder_all.py
RENAMED
|
@@ -40,7 +40,8 @@ class NoDunderAll(Rule):
|
|
|
40
40
|
remediation=(
|
|
41
41
|
"Before deleting `__all__`, replace wildcard-import consumers with explicit imports, retain each intended "
|
|
42
42
|
"binding, and prefix implementation-only bindings with `_`. Use self-aliased imports to mark intentional "
|
|
43
|
-
"re-exports to static tooling."
|
|
43
|
+
"facade re-exports to static tooling. For a whole-module compatibility alias, replace the entry in "
|
|
44
|
+
"`sys.modules` and migrate maintained consumers to the canonical path instead of copying every name."
|
|
44
45
|
),
|
|
45
46
|
category=RuleCategory.MAINTAINABILITY,
|
|
46
47
|
autofix=AutofixPolicy.NONE,
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
from pathlib import PurePosixPath
|
|
5
|
+
from typing import TYPE_CHECKING, ClassVar, final, override
|
|
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
|
+
parse_or_none,
|
|
18
|
+
)
|
|
19
|
+
from sarj_python_lint.rules._paths import is_generated
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
_MIN_ALIAS_STATEMENTS = 4
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@final
|
|
30
|
+
class NoRedundantModuleAliasExports(Rule):
|
|
31
|
+
id: str = "no-redundant-module-alias-exports"
|
|
32
|
+
code: str = "SARJ440"
|
|
33
|
+
documentation: ClassVar[RuleDocumentation | None] = RuleDocumentation(
|
|
34
|
+
summary="Do not copy names before replacing a compatibility module.",
|
|
35
|
+
rationale=(
|
|
36
|
+
"A compatibility module that replaces itself in `sys.modules` already exposes the canonical module. "
|
|
37
|
+
"Copying every public attribute first duplicates the API surface and invites drift."
|
|
38
|
+
),
|
|
39
|
+
remediation=(
|
|
40
|
+
"Delete the forwarding assignments and keep the canonical module import plus the final "
|
|
41
|
+
"`sys.modules[__name__] = alias` replacement. Import the canonical path inside maintained code."
|
|
42
|
+
),
|
|
43
|
+
category=RuleCategory.MAINTAINABILITY,
|
|
44
|
+
autofix=AutofixPolicy.NONE,
|
|
45
|
+
limitations=(
|
|
46
|
+
"Only otherwise-pure compatibility modules with a final, unconditional self-replacement are checked.",
|
|
47
|
+
"Forwarding must use exact same-name assignments such as `Thing = canonical.Thing`.",
|
|
48
|
+
"Conditional, nested, computed, annotated, chained, destructured, or mixed-purpose modules are excluded.",
|
|
49
|
+
"Generated source, malformed source, and stub files (`.pyi`) are outside this rule's scope.",
|
|
50
|
+
"No autofix is offered because attribute access can have side effects and circular imports require review.",
|
|
51
|
+
),
|
|
52
|
+
examples=(
|
|
53
|
+
RuleExample(
|
|
54
|
+
example_id="self-replacing-compatibility-module",
|
|
55
|
+
title="Remove bindings that the canonical module replacement supersedes",
|
|
56
|
+
outcome=ExampleOutcome.MATCH,
|
|
57
|
+
files=(
|
|
58
|
+
ExampleFile.python(
|
|
59
|
+
"legacy/settings.py",
|
|
60
|
+
"import sys\n\n"
|
|
61
|
+
"from canonical import settings as _canonical\n\n\n"
|
|
62
|
+
"Settings = _canonical.Settings\n"
|
|
63
|
+
"load = _canonical.load\n"
|
|
64
|
+
"sys.modules[__name__] = _canonical\n",
|
|
65
|
+
),
|
|
66
|
+
),
|
|
67
|
+
focus_path=PurePosixPath("legacy/settings.py"),
|
|
68
|
+
expected_count=1,
|
|
69
|
+
public=True,
|
|
70
|
+
),
|
|
71
|
+
RuleExample(
|
|
72
|
+
example_id="pure-module-alias",
|
|
73
|
+
title="Replace the compatibility module without copying its attributes",
|
|
74
|
+
outcome=ExampleOutcome.NO_MATCH,
|
|
75
|
+
files=(
|
|
76
|
+
ExampleFile.python(
|
|
77
|
+
"legacy/settings.py",
|
|
78
|
+
"import sys\n\n"
|
|
79
|
+
"from canonical import settings as _canonical\n\n\n"
|
|
80
|
+
"sys.modules[__name__] = _canonical\n",
|
|
81
|
+
),
|
|
82
|
+
),
|
|
83
|
+
focus_path=PurePosixPath("legacy/settings.py"),
|
|
84
|
+
expected_count=0,
|
|
85
|
+
public=True,
|
|
86
|
+
),
|
|
87
|
+
),
|
|
88
|
+
)
|
|
89
|
+
description: str = documentation.summary
|
|
90
|
+
|
|
91
|
+
@override
|
|
92
|
+
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
93
|
+
if path.suffix != ".py" or "sys.modules" not in source or is_generated(path, source):
|
|
94
|
+
return []
|
|
95
|
+
tree = parse_or_none(path, source)
|
|
96
|
+
if tree is None:
|
|
97
|
+
return []
|
|
98
|
+
first_forward = _redundant_forward(tree)
|
|
99
|
+
if first_forward is None:
|
|
100
|
+
return []
|
|
101
|
+
return [
|
|
102
|
+
Diagnostic(
|
|
103
|
+
path=path,
|
|
104
|
+
line=first_forward.lineno,
|
|
105
|
+
col=first_forward.col_offset + 1,
|
|
106
|
+
code=self.code,
|
|
107
|
+
severity=Severity.ERROR,
|
|
108
|
+
message=(
|
|
109
|
+
"Delete the forwarding assignments; this compatibility module already replaces itself with "
|
|
110
|
+
"the canonical module."
|
|
111
|
+
),
|
|
112
|
+
)
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _redundant_forward(tree: ast.Module) -> ast.Assign | None:
|
|
117
|
+
body = list(tree.body)
|
|
118
|
+
if body and _is_docstring(body[0]):
|
|
119
|
+
body.pop(0)
|
|
120
|
+
while body and isinstance(body[0], ast.ImportFrom) and body[0].module == "__future__":
|
|
121
|
+
body.pop(0)
|
|
122
|
+
if len(body) < _MIN_ALIAS_STATEMENTS or not _imports_sys(body[0]):
|
|
123
|
+
return None
|
|
124
|
+
alias = _canonical_alias(body[1])
|
|
125
|
+
if alias is None or not _replaces_current_module(body[-1], alias):
|
|
126
|
+
return None
|
|
127
|
+
forwards = body[2:-1]
|
|
128
|
+
if not forwards:
|
|
129
|
+
return None
|
|
130
|
+
for statement in forwards:
|
|
131
|
+
if not _is_same_name_forward(statement, alias):
|
|
132
|
+
return None
|
|
133
|
+
return forwards[0] if isinstance(forwards[0], ast.Assign) else None
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _is_docstring(statement: ast.stmt) -> bool:
|
|
137
|
+
return (
|
|
138
|
+
isinstance(statement, ast.Expr)
|
|
139
|
+
and isinstance(statement.value, ast.Constant)
|
|
140
|
+
and isinstance(statement.value.value, str)
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _imports_sys(statement: ast.stmt) -> bool:
|
|
145
|
+
return (
|
|
146
|
+
isinstance(statement, ast.Import)
|
|
147
|
+
and len(statement.names) == 1
|
|
148
|
+
and statement.names[0].name == "sys"
|
|
149
|
+
and (statement.names[0].asname is None)
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _canonical_alias(statement: ast.stmt) -> str | None:
|
|
154
|
+
match statement:
|
|
155
|
+
case ast.Import(names=[ast.alias(asname=alias)]) if alias is not None:
|
|
156
|
+
return alias
|
|
157
|
+
case ast.ImportFrom(module=module, names=[ast.alias(name=name, asname=alias)]) if (
|
|
158
|
+
module is not None and name != "*" and alias is not None
|
|
159
|
+
):
|
|
160
|
+
return alias
|
|
161
|
+
case _:
|
|
162
|
+
return None
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _is_same_name_forward(statement: ast.stmt, alias: str) -> bool:
|
|
166
|
+
match statement:
|
|
167
|
+
case ast.Assign(
|
|
168
|
+
targets=[ast.Name(id=target, ctx=ast.Store())],
|
|
169
|
+
value=ast.Attribute(value=ast.Name(id=owner, ctx=ast.Load()), attr=attribute, ctx=ast.Load()),
|
|
170
|
+
):
|
|
171
|
+
return owner == alias and target == attribute and target not in {"sys", "__name__", alias}
|
|
172
|
+
case _:
|
|
173
|
+
return False
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _replaces_current_module(statement: ast.stmt, alias: str) -> bool:
|
|
177
|
+
match statement:
|
|
178
|
+
case ast.Assign(
|
|
179
|
+
targets=[
|
|
180
|
+
ast.Subscript(
|
|
181
|
+
value=ast.Attribute(value=ast.Name(id="sys", ctx=ast.Load()), attr="modules", ctx=ast.Load()),
|
|
182
|
+
slice=ast.Name(id="__name__", ctx=ast.Load()),
|
|
183
|
+
ctx=ast.Store(),
|
|
184
|
+
)
|
|
185
|
+
],
|
|
186
|
+
value=ast.Name(id=replacement, ctx=ast.Load()),
|
|
187
|
+
):
|
|
188
|
+
return replacement == alias
|
|
189
|
+
case _:
|
|
190
|
+
return False
|
|
@@ -101,6 +101,7 @@ class PreferMatchTypeDispatch(Rule):
|
|
|
101
101
|
limitations=(
|
|
102
102
|
"Only three or more adjacent, unguarded `isinstance` branches over the same simple name are checked.",
|
|
103
103
|
"The checked types must be unshadowed builtins, unshadowed module-local classes, or proven stdlib ast classes; unresolved imports, runtime type groups, repeated type references, generated files, and non-terminating sibling checks are excluded.",
|
|
104
|
+
"A terminal-looking context-manager body does not prove a sibling branch terminates: exceptions can be suppressed. An unconditional return or raise after the context manager remains eligible.",
|
|
104
105
|
"Declared support for Python before 3.10 suppresses this recommendation when proven by the nearest project metadata or exact installed-distribution ownership. Missing or ambiguous target metadata retains advisory behavior; it does not prove a modern target.",
|
|
105
106
|
),
|
|
106
107
|
examples=(
|
|
@@ -422,8 +423,6 @@ def _body_terminates(body: list[ast.stmt]) -> bool:
|
|
|
422
423
|
return True
|
|
423
424
|
case ast.If(body=if_body, orelse=else_body):
|
|
424
425
|
return bool(else_body) and _body_terminates(if_body) and _body_terminates(else_body)
|
|
425
|
-
case ast.With() | ast.AsyncWith():
|
|
426
|
-
return _body_terminates(last.body)
|
|
427
426
|
case _:
|
|
428
427
|
return False
|
|
429
428
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/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.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/_ast_index.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/_docstrings.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/_first_party.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/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.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/_project_index.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/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.0 → sarj_python_lint-0.81.2}/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
|
{sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/mock_without_spec.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/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
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/no_secret_in_log.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/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
|
{sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/over_mocked_test.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/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
|
{sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/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.81.0 → sarj_python_lint-0.81.2}/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
|
{sarj_python_lint-0.81.0 → sarj_python_lint-0.81.2}/src/sarj_python_lint/rules/unused_mock_setup.py
RENAMED
|
File without changes
|