sarj-python-lint 0.82.0__tar.gz → 0.82.1__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.82.0 → sarj_python_lint-0.82.1}/PKG-INFO +1 -1
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/pyproject.toml +1 -1
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +98 -15
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/.gitignore +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/LICENSE +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/README.md +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/_analysis_session.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/_filesystem.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/_python_target.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/_comments.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/_docstrings.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/_fastapi.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/_imports.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/_project_index.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/_registry.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_deleted_only_override_parameter.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/pydantic_at_boundaries.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
- {sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/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.82.
|
|
3
|
+
Version: 0.82.1
|
|
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/
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import ast
|
|
4
4
|
from pathlib import PurePosixPath
|
|
5
|
+
import re
|
|
5
6
|
from typing import TYPE_CHECKING, ClassVar, final, override
|
|
6
7
|
|
|
7
8
|
from sarj_python_lint.rule_base import (
|
|
@@ -24,6 +25,7 @@ if TYPE_CHECKING:
|
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
_SYS_SOURCES = frozenset({"sys"})
|
|
28
|
+
_PRIVATE_ALIAS_CANDIDATE_RE = re.compile(r"(?m)^(?![\d_])\w+(?:\s*:[^=\n]+)?(?:\s*=\s*(?!_)\w+)*\s*=\s*_(?!_)\w")
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
@final
|
|
@@ -31,25 +33,67 @@ class NoRedundantModuleAliasExports(Rule):
|
|
|
31
33
|
id: str = "no-redundant-module-alias-exports"
|
|
32
34
|
code: str = "SARJ440"
|
|
33
35
|
documentation: ClassVar[RuleDocumentation | None] = RuleDocumentation(
|
|
34
|
-
summary="Do not replace the current module
|
|
36
|
+
summary="Do not manufacture public APIs from private names or replace the current module.",
|
|
35
37
|
rationale=(
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
+
"Binding a public name to a private implementation creates two names for one API and disguises the "
|
|
39
|
+
"intended visibility boundary. Replacing the current module similarly mutates import identity at runtime "
|
|
40
|
+
"in a way static tools cannot model. Both forms make navigation, introspection, and compatibility ownership "
|
|
41
|
+
"surprising."
|
|
38
42
|
),
|
|
39
43
|
remediation=(
|
|
40
|
-
"
|
|
41
|
-
"
|
|
44
|
+
"Define maintained APIs under their public names and update local callers directly. Import the canonical "
|
|
45
|
+
"path inside maintained code. If a compatibility module is still required, expose its supported names with "
|
|
46
|
+
"explicit same-name imports grouped by source module."
|
|
42
47
|
),
|
|
43
48
|
category=RuleCategory.MAINTAINABILITY,
|
|
44
49
|
autofix=AutofixPolicy.NONE,
|
|
45
50
|
limitations=(
|
|
46
|
-
|
|
47
|
-
|
|
51
|
+
(
|
|
52
|
+
"Public-to-private name aliases are checked only as direct module-body assignments; assignments "
|
|
53
|
+
"through an unconditional, unshadowed stdlib `sys` import include module-level control flow."
|
|
54
|
+
),
|
|
55
|
+
"Aliases inside module control flow, functions, classes, and `TYPE_CHECKING` branches are excluded.",
|
|
56
|
+
(
|
|
57
|
+
"Private targets, dunder sources, attribute values, destructuring, imports, and same-visibility aliases "
|
|
58
|
+
"are excluded."
|
|
59
|
+
),
|
|
48
60
|
"Literal-key registrations, child-module registrations, method calls, reads, and arbitrary registries are excluded.",
|
|
49
61
|
"Generated source, malformed source, and stub files (`.pyi`) are outside this rule's scope.",
|
|
50
62
|
"No autofix is offered because the intended compatibility surface and identity requirements need review.",
|
|
51
63
|
),
|
|
52
64
|
examples=(
|
|
65
|
+
RuleExample(
|
|
66
|
+
example_id="public-alias-for-private-helper",
|
|
67
|
+
title="Define a public helper under its public name",
|
|
68
|
+
outcome=ExampleOutcome.MATCH,
|
|
69
|
+
files=(
|
|
70
|
+
ExampleFile.python(
|
|
71
|
+
"pagination.py",
|
|
72
|
+
"def _encode_cursor(value: str) -> str:\n"
|
|
73
|
+
" return value\n\n\n"
|
|
74
|
+
"encode_phone_number_cursor = _encode_cursor\n",
|
|
75
|
+
),
|
|
76
|
+
),
|
|
77
|
+
focus_path=PurePosixPath("pagination.py"),
|
|
78
|
+
expected_count=1,
|
|
79
|
+
public=True,
|
|
80
|
+
scenario="public-private-alias",
|
|
81
|
+
),
|
|
82
|
+
RuleExample(
|
|
83
|
+
example_id="public-helper-definition",
|
|
84
|
+
title="Give the implementation its intended public name",
|
|
85
|
+
outcome=ExampleOutcome.NO_MATCH,
|
|
86
|
+
files=(
|
|
87
|
+
ExampleFile.python(
|
|
88
|
+
"pagination.py",
|
|
89
|
+
"def encode_phone_number_cursor(value: str) -> str:\n return value\n",
|
|
90
|
+
),
|
|
91
|
+
),
|
|
92
|
+
focus_path=PurePosixPath("pagination.py"),
|
|
93
|
+
expected_count=0,
|
|
94
|
+
public=True,
|
|
95
|
+
scenario="public-private-alias",
|
|
96
|
+
),
|
|
53
97
|
RuleExample(
|
|
54
98
|
example_id="current-module-replacement",
|
|
55
99
|
title="Do not replace a compatibility module at runtime",
|
|
@@ -65,6 +109,7 @@ class NoRedundantModuleAliasExports(Rule):
|
|
|
65
109
|
focus_path=PurePosixPath("legacy/settings.py"),
|
|
66
110
|
expected_count=1,
|
|
67
111
|
public=True,
|
|
112
|
+
scenario="module-replacement",
|
|
68
113
|
),
|
|
69
114
|
RuleExample(
|
|
70
115
|
example_id="explicit-compatibility-exports",
|
|
@@ -79,6 +124,7 @@ class NoRedundantModuleAliasExports(Rule):
|
|
|
79
124
|
focus_path=PurePosixPath("legacy/settings.py"),
|
|
80
125
|
expected_count=0,
|
|
81
126
|
public=True,
|
|
127
|
+
scenario="module-replacement",
|
|
82
128
|
),
|
|
83
129
|
),
|
|
84
130
|
)
|
|
@@ -86,14 +132,17 @@ class NoRedundantModuleAliasExports(Rule):
|
|
|
86
132
|
|
|
87
133
|
@override
|
|
88
134
|
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
89
|
-
|
|
135
|
+
has_module_replacement_candidate = "modules" in source and "__name__" in source
|
|
136
|
+
if (
|
|
137
|
+
path.suffix != ".py"
|
|
138
|
+
or (not has_module_replacement_candidate and _PRIVATE_ALIAS_CANDIDATE_RE.search(source) is None)
|
|
139
|
+
or is_generated(path, source)
|
|
140
|
+
):
|
|
90
141
|
return []
|
|
91
142
|
tree = parse_or_none(path, source)
|
|
92
143
|
if tree is None:
|
|
93
144
|
return []
|
|
94
145
|
imports = ImportIndex.from_tree(tree, module_scope_only=True)
|
|
95
|
-
if not imports.builtin_is_unshadowed("__name__"):
|
|
96
|
-
return []
|
|
97
146
|
direct_sys_bindings = _direct_sys_bindings(tree)
|
|
98
147
|
findings = [
|
|
99
148
|
Diagnostic(
|
|
@@ -102,17 +151,51 @@ class NoRedundantModuleAliasExports(Rule):
|
|
|
102
151
|
col=target.col_offset + 1,
|
|
103
152
|
code=self.code,
|
|
104
153
|
message=(
|
|
105
|
-
"Do not
|
|
106
|
-
"
|
|
154
|
+
f"Do not expose private `{source_name}` as public `{target.id}`; define the implementation under "
|
|
155
|
+
"its public name and update local callers directly."
|
|
107
156
|
),
|
|
108
157
|
)
|
|
109
|
-
for statement in
|
|
110
|
-
for target in
|
|
111
|
-
if _is_current_module_target(target, imports, direct_sys_bindings)
|
|
158
|
+
for statement in tree.body
|
|
159
|
+
for target, source_name in _public_private_aliases(statement)
|
|
112
160
|
]
|
|
161
|
+
if imports.builtin_is_unshadowed("__name__"):
|
|
162
|
+
findings.extend(
|
|
163
|
+
Diagnostic(
|
|
164
|
+
path=path,
|
|
165
|
+
line=target.lineno,
|
|
166
|
+
col=target.col_offset + 1,
|
|
167
|
+
code=self.code,
|
|
168
|
+
message=(
|
|
169
|
+
"Do not replace the current module through `sys.modules[__name__]`; expose compatibility names "
|
|
170
|
+
"with explicit imports and update maintained callers to the canonical module."
|
|
171
|
+
),
|
|
172
|
+
)
|
|
173
|
+
for statement in _module_statements(tree.body)
|
|
174
|
+
for target in _assignment_targets(statement)
|
|
175
|
+
if _is_current_module_target(target, imports, direct_sys_bindings)
|
|
176
|
+
)
|
|
113
177
|
return sorted(findings, key=lambda finding: (finding.line, finding.col, finding.code))
|
|
114
178
|
|
|
115
179
|
|
|
180
|
+
def _public_private_aliases(statement: ast.stmt) -> list[tuple[ast.Name, str]]:
|
|
181
|
+
match statement:
|
|
182
|
+
case ast.Assign(targets=targets, value=ast.Name(id=source_name)):
|
|
183
|
+
pass
|
|
184
|
+
case ast.AnnAssign(target=target, value=ast.Name(id=source_name)):
|
|
185
|
+
targets = [target]
|
|
186
|
+
case _:
|
|
187
|
+
return []
|
|
188
|
+
if not _is_private_name(source_name):
|
|
189
|
+
return []
|
|
190
|
+
return [
|
|
191
|
+
(target, source_name) for target in targets if isinstance(target, ast.Name) and not target.id.startswith("_")
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _is_private_name(name: str) -> bool:
|
|
196
|
+
return len(name) > 1 and name.startswith("_") and not name.startswith("__")
|
|
197
|
+
|
|
198
|
+
|
|
116
199
|
def _direct_sys_bindings(tree: ast.Module) -> frozenset[str]:
|
|
117
200
|
bindings: set[str] = set()
|
|
118
201
|
for statement in tree.body:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/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.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/_ast_index.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/_docstrings.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/_first_party.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/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.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/_project_index.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/_prose_budget.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/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.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/mock_without_spec.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/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
|
{sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/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
|
{sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/no_secret_in_log.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/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
|
{sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/over_mocked_test.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/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
|
{sarj_python_lint-0.82.0 → sarj_python_lint-0.82.1}/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.82.0 → sarj_python_lint-0.82.1}/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.82.0 → sarj_python_lint-0.82.1}/src/sarj_python_lint/rules/unused_mock_setup.py
RENAMED
|
File without changes
|
|
File without changes
|