sarj-python-lint 0.81.8__tar.gz → 0.81.11__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.8 → sarj_python_lint-0.81.11}/PKG-INFO +1 -1
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/pyproject.toml +1 -1
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +23 -7
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +4 -151
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +57 -24
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/.gitignore +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/LICENSE +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/README.md +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/_analysis_session.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/_filesystem.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/_python_target.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_comments.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_docstrings.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_fastapi.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_imports.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_project_index.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_registry.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/pydantic_at_boundaries.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
- {sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/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.11
|
|
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/
|
|
@@ -42,7 +42,7 @@ class NoRawConnectionInTests(Rule):
|
|
|
42
42
|
category=RuleCategory.TESTING,
|
|
43
43
|
autofix=AutofixPolicy.NONE,
|
|
44
44
|
limitations=(
|
|
45
|
-
"Only test
|
|
45
|
+
"Only collected test modules outside conftest.py, conventional shared test-support modules, and migration-test trees are inspected.",
|
|
46
46
|
"A receiver is reported only when a parameter, annotated local, or constructor call proves it is a psycopg ConnectionPool or AsyncConnectionPool.",
|
|
47
47
|
"Pytest fixtures may use a connection internally for setup and cleanup, but fixtures that return or yield the connection remain reportable.",
|
|
48
48
|
),
|
|
@@ -81,12 +81,16 @@ class NoRawConnectionInTests(Rule):
|
|
|
81
81
|
|
|
82
82
|
@override
|
|
83
83
|
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
84
|
+
excluded_path = any(
|
|
85
|
+
(
|
|
86
|
+
not is_test_path(path),
|
|
87
|
+
path.name == "conftest.py",
|
|
88
|
+
is_test_support_path(path),
|
|
89
|
+
_is_non_collected_test_support(path),
|
|
90
|
+
_is_migration_test(path),
|
|
91
|
+
)
|
|
92
|
+
)
|
|
93
|
+
if excluded_path or is_generated(path, source):
|
|
90
94
|
return []
|
|
91
95
|
tree = parse_or_none(path, source)
|
|
92
96
|
if tree is None:
|
|
@@ -120,6 +124,18 @@ class NoRawConnectionInTests(Rule):
|
|
|
120
124
|
return sorted(diagnostics, key=lambda item: (item.line, item.col))
|
|
121
125
|
|
|
122
126
|
|
|
127
|
+
def _is_non_collected_test_support(path: Path) -> bool:
|
|
128
|
+
return "tests" in path.parts and not (path.name.startswith("test_") or path.name.endswith("_test.py"))
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _is_migration_test(path: Path) -> bool:
|
|
132
|
+
parts = path.parts
|
|
133
|
+
return any(
|
|
134
|
+
part == "tests" and index + 1 < len(parts) and parts[index + 1] == "migrations"
|
|
135
|
+
for index, part in enumerate(parts)
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
123
139
|
def _proven_pool_names(scope: ast.Module | ast.FunctionDef | ast.AsyncFunctionDef) -> frozenset[str]:
|
|
124
140
|
names: set[str] = set()
|
|
125
141
|
if isinstance(scope, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
@@ -26,7 +26,6 @@ if TYPE_CHECKING:
|
|
|
26
26
|
|
|
27
27
|
_MIN_BRANCHES = 3
|
|
28
28
|
_ISINSTANCE_ARGS = 2
|
|
29
|
-
_NESTED_TYPE_CHECKS = 2
|
|
30
29
|
_BUILTIN_TYPES = frozenset(
|
|
31
30
|
{
|
|
32
31
|
"bool",
|
|
@@ -88,24 +87,21 @@ class PreferMatchTypeDispatch(Rule):
|
|
|
88
87
|
id: str = "prefer-match-type-dispatch"
|
|
89
88
|
code: str = "SARJ080"
|
|
90
89
|
documentation: ClassVar[RuleDocumentation | None] = RuleDocumentation(
|
|
91
|
-
summary="Prefer `match` for structural runtime type dispatch
|
|
90
|
+
summary="Prefer `match` for structural runtime type dispatch.",
|
|
92
91
|
rationale=(
|
|
93
92
|
"A long mutually exclusive `isinstance` dispatch repeats its subject and hides the closed list of "
|
|
94
|
-
"runtime shapes.
|
|
95
|
-
"spells one structural invariant as unrelated boolean calls; class patterns express both shapes and "
|
|
96
|
-
"can bind the narrowed values in one construct."
|
|
93
|
+
"runtime shapes. Class patterns make the alternatives explicit without repeating the dispatch subject."
|
|
97
94
|
),
|
|
98
95
|
remediation=(
|
|
99
96
|
"Replace the branches with `match subject` and one class-pattern arm per distinct type; combine types "
|
|
100
|
-
"that share behavior with an OR-pattern.
|
|
101
|
-
"narrowed attribute that later code consumes."
|
|
97
|
+
"that share behavior with an OR-pattern."
|
|
102
98
|
),
|
|
103
99
|
category=RuleCategory.MAINTAINABILITY,
|
|
104
100
|
autofix=AutofixPolicy.NONE,
|
|
105
101
|
limitations=(
|
|
106
102
|
"General dispatch requires three or more adjacent, unguarded `isinstance` branches over the same simple name. Two arms are checked only for an exact `ast.Name.id` / `ast.Attribute.attr` projection followed by a None-returning fallback.",
|
|
107
|
-
"Nested-guard findings require exactly two negated checks joined by `or`: an imported or module-local class for a simple name, followed by an imported or module-local class for an attribute rooted at that name, with an unconditional built-in `TypeError` raise.",
|
|
108
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
|
+
"Nested attribute-validation guards are excluded: converting them to keyword class patterns can turn attribute errors into match fallthrough, and an imported isinstance operand may be a runtime tuple rather than a class.",
|
|
109
105
|
"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.",
|
|
110
106
|
"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.",
|
|
111
107
|
),
|
|
@@ -154,47 +150,6 @@ class PreferMatchTypeDispatch(Rule):
|
|
|
154
150
|
expected_count=0,
|
|
155
151
|
public=True,
|
|
156
152
|
),
|
|
157
|
-
RuleExample(
|
|
158
|
-
example_id="nested-class-type-guard",
|
|
159
|
-
title="Separate type checks describe one nested runtime shape",
|
|
160
|
-
outcome=ExampleOutcome.MATCH,
|
|
161
|
-
scenario="nested-class-guard",
|
|
162
|
-
files=(
|
|
163
|
-
ExampleFile.python(
|
|
164
|
-
"app/scheduler.py",
|
|
165
|
-
"from app.models import ActiveSettings, CustomScenario\n\n"
|
|
166
|
-
"def schedule(settings):\n"
|
|
167
|
-
" if not isinstance(settings, ActiveSettings) or not isinstance(settings.scenario, CustomScenario):\n"
|
|
168
|
-
" raise TypeError('custom scenario required')\n"
|
|
169
|
-
" return settings.scenario.id\n",
|
|
170
|
-
),
|
|
171
|
-
),
|
|
172
|
-
focus_path=PurePosixPath("app/scheduler.py"),
|
|
173
|
-
expected_count=1,
|
|
174
|
-
public=True,
|
|
175
|
-
),
|
|
176
|
-
RuleExample(
|
|
177
|
-
example_id="nested-class-pattern",
|
|
178
|
-
title="One class pattern checks and binds the nested runtime shape",
|
|
179
|
-
outcome=ExampleOutcome.NO_MATCH,
|
|
180
|
-
scenario="nested-class-guard",
|
|
181
|
-
files=(
|
|
182
|
-
ExampleFile.python(
|
|
183
|
-
"app/scheduler.py",
|
|
184
|
-
"from app.models import ActiveSettings, CustomScenario\n\n"
|
|
185
|
-
"def schedule(settings):\n"
|
|
186
|
-
" match settings:\n"
|
|
187
|
-
" case ActiveSettings(scenario=CustomScenario() as scenario):\n"
|
|
188
|
-
" pass\n"
|
|
189
|
-
" case _:\n"
|
|
190
|
-
" raise TypeError('custom scenario required')\n"
|
|
191
|
-
" return scenario.id\n",
|
|
192
|
-
),
|
|
193
|
-
),
|
|
194
|
-
focus_path=PurePosixPath("app/scheduler.py"),
|
|
195
|
-
expected_count=0,
|
|
196
|
-
public=True,
|
|
197
|
-
),
|
|
198
153
|
RuleExample(
|
|
199
154
|
example_id="two-arm-ast-projection",
|
|
200
155
|
title="Two isinstance returns project one AST identifier shape",
|
|
@@ -277,16 +232,6 @@ class PreferMatchTypeDispatch(Rule):
|
|
|
277
232
|
unsafe_bindings=unsafe_bindings,
|
|
278
233
|
)
|
|
279
234
|
)
|
|
280
|
-
findings.extend(
|
|
281
|
-
_nested_guard_findings(
|
|
282
|
-
all_nodes,
|
|
283
|
-
path,
|
|
284
|
-
self.code,
|
|
285
|
-
imports,
|
|
286
|
-
local_classes,
|
|
287
|
-
unsafe_bindings=unsafe_bindings,
|
|
288
|
-
)
|
|
289
|
-
)
|
|
290
235
|
findings.extend(
|
|
291
236
|
_two_arm_ast_projection_findings(
|
|
292
237
|
all_nodes,
|
|
@@ -441,62 +386,6 @@ def _dispatch(
|
|
|
441
386
|
return _Dispatch(subjects.pop(), len(branches))
|
|
442
387
|
|
|
443
388
|
|
|
444
|
-
def _nested_guard_findings(
|
|
445
|
-
all_nodes: tuple[ast.AST, ...],
|
|
446
|
-
path: Path,
|
|
447
|
-
code: str,
|
|
448
|
-
imports: ImportIndex,
|
|
449
|
-
local_classes: frozenset[str],
|
|
450
|
-
*,
|
|
451
|
-
unsafe_bindings: frozenset[str],
|
|
452
|
-
) -> list[Diagnostic]:
|
|
453
|
-
findings: list[Diagnostic] = []
|
|
454
|
-
for node in all_nodes:
|
|
455
|
-
if (
|
|
456
|
-
not isinstance(node, ast.If)
|
|
457
|
-
or node.orelse
|
|
458
|
-
or not _raises_builtin_type_error(node.body, imports, unsafe_bindings)
|
|
459
|
-
or "isinstance" in unsafe_bindings
|
|
460
|
-
):
|
|
461
|
-
continue
|
|
462
|
-
test = node.test
|
|
463
|
-
if (
|
|
464
|
-
not isinstance(test, ast.BoolOp)
|
|
465
|
-
or not isinstance(test.op, ast.Or)
|
|
466
|
-
or len(test.values) != _NESTED_TYPE_CHECKS
|
|
467
|
-
):
|
|
468
|
-
continue
|
|
469
|
-
first = _negated_isinstance(test.values[0])
|
|
470
|
-
second = _negated_isinstance(test.values[1])
|
|
471
|
-
if first is None or second is None:
|
|
472
|
-
continue
|
|
473
|
-
first_subject, first_type = first
|
|
474
|
-
second_subject, second_type = second
|
|
475
|
-
if not isinstance(first_subject, ast.Name):
|
|
476
|
-
continue
|
|
477
|
-
attribute = _attribute_path(second_subject, root=first_subject.id)
|
|
478
|
-
if (
|
|
479
|
-
attribute is None
|
|
480
|
-
or not _is_class_pattern_type(first_type, imports, local_classes)
|
|
481
|
-
or not _is_class_pattern_type(second_type, imports, local_classes)
|
|
482
|
-
):
|
|
483
|
-
continue
|
|
484
|
-
findings.append(
|
|
485
|
-
Diagnostic(
|
|
486
|
-
path=path,
|
|
487
|
-
line=node.lineno,
|
|
488
|
-
col=node.col_offset + 1,
|
|
489
|
-
code=code,
|
|
490
|
-
severity=Severity.WARNING,
|
|
491
|
-
message=(
|
|
492
|
-
f"nested isinstance guard on '{first_subject.id}.{attribute}' — use one match/case class "
|
|
493
|
-
"pattern and bind the narrowed attribute consumed after the guard."
|
|
494
|
-
),
|
|
495
|
-
)
|
|
496
|
-
)
|
|
497
|
-
return findings
|
|
498
|
-
|
|
499
|
-
|
|
500
389
|
def _two_arm_ast_projection_findings(
|
|
501
390
|
all_nodes: tuple[ast.AST, ...],
|
|
502
391
|
path: Path,
|
|
@@ -602,42 +491,6 @@ def _returns_none(statement: ast.stmt) -> bool:
|
|
|
602
491
|
)
|
|
603
492
|
|
|
604
493
|
|
|
605
|
-
def _raises_builtin_type_error(body: list[ast.stmt], imports: ImportIndex, unsafe_bindings: frozenset[str]) -> bool:
|
|
606
|
-
if not imports.builtin_is_unshadowed("TypeError") or "TypeError" in unsafe_bindings or not body:
|
|
607
|
-
return False
|
|
608
|
-
final_statement = body[-1]
|
|
609
|
-
if not isinstance(final_statement, ast.Raise) or final_statement.exc is None:
|
|
610
|
-
return False
|
|
611
|
-
exception = final_statement.exc.func if isinstance(final_statement.exc, ast.Call) else final_statement.exc
|
|
612
|
-
return isinstance(exception, ast.Name) and exception.id == "TypeError"
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
def _negated_isinstance(node: ast.expr) -> tuple[ast.expr, ast.expr] | None:
|
|
616
|
-
if not isinstance(node, ast.UnaryOp) or not isinstance(node.op, ast.Not):
|
|
617
|
-
return None
|
|
618
|
-
call = node.operand
|
|
619
|
-
if not (
|
|
620
|
-
isinstance(call, ast.Call)
|
|
621
|
-
and isinstance(call.func, ast.Name)
|
|
622
|
-
and call.func.id == "isinstance"
|
|
623
|
-
and len(call.args) == _ISINSTANCE_ARGS
|
|
624
|
-
and not call.keywords
|
|
625
|
-
):
|
|
626
|
-
return None
|
|
627
|
-
return call.args[0], call.args[1]
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
def _attribute_path(node: ast.expr, *, root: str) -> str | None:
|
|
631
|
-
parts: list[str] = []
|
|
632
|
-
current = node
|
|
633
|
-
while isinstance(current, ast.Attribute):
|
|
634
|
-
parts.append(current.attr)
|
|
635
|
-
current = current.value
|
|
636
|
-
if not isinstance(current, ast.Name) or current.id != root or not parts:
|
|
637
|
-
return None
|
|
638
|
-
return ".".join(reversed(parts))
|
|
639
|
-
|
|
640
|
-
|
|
641
494
|
def _is_class_pattern_type(
|
|
642
495
|
node: ast.expr,
|
|
643
496
|
imports: ImportIndex,
|
|
@@ -5,7 +5,7 @@ from io import StringIO
|
|
|
5
5
|
from itertools import pairwise
|
|
6
6
|
from pathlib import PurePosixPath
|
|
7
7
|
import tokenize
|
|
8
|
-
from typing import TYPE_CHECKING, ClassVar, NamedTuple, final, override
|
|
8
|
+
from typing import TYPE_CHECKING, ClassVar, Literal, NamedTuple, final, override
|
|
9
9
|
|
|
10
10
|
from sarj_python_lint.rule_base import (
|
|
11
11
|
AutofixPolicy,
|
|
@@ -28,11 +28,13 @@ if TYPE_CHECKING:
|
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
_MAX_COMBINED_LINE_LENGTH = 120
|
|
31
|
+
type _NameFlow = Literal["ambiguous", "loaded", "none", "rebound", "terminal"]
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
class _WalrusCandidate(NamedTuple):
|
|
34
35
|
name: str
|
|
35
36
|
awaited: ast.Await
|
|
37
|
+
is_not_none: bool
|
|
36
38
|
|
|
37
39
|
|
|
38
40
|
@final
|
|
@@ -40,20 +42,23 @@ class PreferWalrusAwaitedNoneGuard(Rule):
|
|
|
40
42
|
id = "prefer-walrus-awaited-none-guard"
|
|
41
43
|
code = "SARJ432"
|
|
42
44
|
documentation: ClassVar[RuleDocumentation | None] = RuleDocumentation(
|
|
43
|
-
summary="Bind a compact awaited lookup in its immediately following None guard.",
|
|
45
|
+
summary="Bind a compact awaited lookup in its immediately following terminal None guard.",
|
|
44
46
|
rationale=(
|
|
45
|
-
"When an awaited lookup and its
|
|
46
|
-
"the operation
|
|
47
|
+
"When an awaited lookup and its terminal None guard are adjacent, binding in the condition keeps "
|
|
48
|
+
"the operation, branch decision, and temporary name in one compact expression."
|
|
47
49
|
),
|
|
48
50
|
remediation=(
|
|
49
51
|
"Rewrite `value = await lookup(); if value is None: return` as "
|
|
50
|
-
"`if (value := await lookup()) is None: return`."
|
|
52
|
+
"`if (value := await lookup()) is None: return`. Preserve `is not None` when the terminal branch "
|
|
53
|
+
"returns or raises with the bound value. Keep two statements when the assignment is an intentional "
|
|
54
|
+
"debugging or tracing boundary."
|
|
51
55
|
),
|
|
52
56
|
category=RuleCategory.STYLE,
|
|
53
57
|
autofix=AutofixPolicy.NONE,
|
|
54
58
|
limitations=(
|
|
55
|
-
"Only one-line awaited calls followed immediately by an exact `is None` guard containing one return are checked.",
|
|
56
|
-
"
|
|
59
|
+
"Only one-line awaited calls followed immediately by an exact `is None` or `is not None` guard containing one return or raise are checked.",
|
|
60
|
+
"A negative guard requires a later use on the surviving path; a positive guard must consume the binding in its terminal statement and leave no later use before rebinding.",
|
|
61
|
+
"The combined condition must fit 120 columns; comments, else branches, ambiguous name flow, generated files, and broader assignment-expression preferences are excluded.",
|
|
57
62
|
),
|
|
58
63
|
examples=(
|
|
59
64
|
RuleExample(
|
|
@@ -99,6 +104,8 @@ class PreferWalrusAwaitedNoneGuard(Rule):
|
|
|
99
104
|
def check(self, path: Path, source: str) -> list[Diagnostic]:
|
|
100
105
|
if path.suffix == ".pyi" or is_generated(path, source):
|
|
101
106
|
return []
|
|
107
|
+
if any(token not in source for token in ("await", "None", "if", "=")):
|
|
108
|
+
return []
|
|
102
109
|
tree = parse_or_none(path, source)
|
|
103
110
|
if tree is None:
|
|
104
111
|
return []
|
|
@@ -110,8 +117,11 @@ class PreferWalrusAwaitedNoneGuard(Rule):
|
|
|
110
117
|
candidate = _candidate(assignment, guard, source, source_lines, comment_lines)
|
|
111
118
|
if candidate is None:
|
|
112
119
|
continue
|
|
113
|
-
name, awaited = candidate
|
|
114
|
-
|
|
120
|
+
name, awaited, is_not_none = candidate
|
|
121
|
+
later_flow = _name_flow(body[index + 2 :], name)
|
|
122
|
+
if (is_not_none and later_flow in {"ambiguous", "loaded"}) or (
|
|
123
|
+
not is_not_none and later_flow != "loaded"
|
|
124
|
+
):
|
|
115
125
|
continue
|
|
116
126
|
if is_suppressed(source_lines, assignment.lineno, self.code) or is_suppressed(
|
|
117
127
|
source_lines, guard.lineno, self.code
|
|
@@ -128,8 +138,9 @@ class PreferWalrusAwaitedNoneGuard(Rule):
|
|
|
128
138
|
code=self.code,
|
|
129
139
|
severity=Severity.WARNING,
|
|
130
140
|
message=(
|
|
131
|
-
f"`{name}` only separates an awaited lookup from its None guard; "
|
|
132
|
-
f"bind it as `if ({name} := {expression.strip()})
|
|
141
|
+
f"`{name}` only separates an awaited lookup from its terminal None guard; "
|
|
142
|
+
f"bind it as `if ({name} := {expression.strip()}) "
|
|
143
|
+
f"is {'not ' if is_not_none else ''}None:`"
|
|
133
144
|
),
|
|
134
145
|
)
|
|
135
146
|
)
|
|
@@ -157,36 +168,50 @@ def _candidate(
|
|
|
157
168
|
and guard.test.end_lineno == guard.lineno
|
|
158
169
|
and not guard.orelse
|
|
159
170
|
and len(guard.body) == 1
|
|
160
|
-
and isinstance(guard.body[0], ast.Return)
|
|
171
|
+
and isinstance(guard.body[0], (ast.Return, ast.Raise))
|
|
161
172
|
):
|
|
162
173
|
return None
|
|
163
174
|
name = assignment.targets[0].id
|
|
164
|
-
|
|
175
|
+
is_not_none = _none_guard_kind(guard.test, name)
|
|
176
|
+
if is_not_none is None or _loads_name(assignment.value, name):
|
|
177
|
+
return None
|
|
178
|
+
terminal = guard.body[0]
|
|
179
|
+
usage = _NameUsage(name)
|
|
180
|
+
usage.visit(terminal)
|
|
181
|
+
if usage.rebound or (is_not_none != usage.loaded):
|
|
165
182
|
return None
|
|
166
|
-
|
|
183
|
+
terminal_end = terminal.end_lineno or terminal.lineno
|
|
184
|
+
if comment_lines.intersection(range(assignment.lineno, terminal_end + 1)):
|
|
167
185
|
return None
|
|
168
186
|
expression = ast.get_source_segment(source, assignment.value)
|
|
169
187
|
if expression is None:
|
|
170
188
|
return None
|
|
171
189
|
original_line = source_lines[assignment.lineno - 1]
|
|
172
190
|
indentation = original_line[: len(original_line) - len(original_line.lstrip())].expandtabs(8)
|
|
173
|
-
combined = f"{indentation}if ({name} := {expression.strip()}) is None:"
|
|
191
|
+
combined = f"{indentation}if ({name} := {expression.strip()}) is {'not ' if is_not_none else ''}None:"
|
|
192
|
+
if terminal.lineno == guard.lineno:
|
|
193
|
+
terminal_source = ast.get_source_segment(source, terminal)
|
|
194
|
+
if terminal_source is None:
|
|
195
|
+
return None
|
|
196
|
+
combined = f"{combined} {terminal_source.strip()}"
|
|
174
197
|
if len(combined) > _MAX_COMBINED_LINE_LENGTH:
|
|
175
198
|
return None
|
|
176
|
-
return _WalrusCandidate(name, assignment.value)
|
|
199
|
+
return _WalrusCandidate(name, assignment.value, is_not_none)
|
|
177
200
|
|
|
178
201
|
|
|
179
|
-
def
|
|
180
|
-
|
|
202
|
+
def _none_guard_kind(test: ast.expr, name: str) -> bool | None:
|
|
203
|
+
if not (
|
|
181
204
|
isinstance(test, ast.Compare)
|
|
182
205
|
and isinstance(test.left, ast.Name)
|
|
183
206
|
and test.left.id == name
|
|
184
207
|
and len(test.ops) == 1
|
|
185
|
-
and isinstance(test.ops[0], ast.Is)
|
|
208
|
+
and isinstance(test.ops[0], (ast.Is, ast.IsNot))
|
|
186
209
|
and len(test.comparators) == 1
|
|
187
210
|
and isinstance(test.comparators[0], ast.Constant)
|
|
188
211
|
and test.comparators[0].value is None
|
|
189
|
-
)
|
|
212
|
+
):
|
|
213
|
+
return None
|
|
214
|
+
return isinstance(test.ops[0], ast.IsNot)
|
|
190
215
|
|
|
191
216
|
|
|
192
217
|
def _loads_name(node: ast.AST, name: str) -> bool:
|
|
@@ -195,15 +220,19 @@ def _loads_name(node: ast.AST, name: str) -> bool:
|
|
|
195
220
|
)
|
|
196
221
|
|
|
197
222
|
|
|
198
|
-
def
|
|
223
|
+
def _name_flow(statements: list[ast.stmt], name: str) -> _NameFlow:
|
|
199
224
|
for statement in statements:
|
|
200
225
|
usage = _NameUsage(name)
|
|
201
226
|
usage.visit(statement)
|
|
227
|
+
if usage.loaded and usage.rebound:
|
|
228
|
+
return "ambiguous"
|
|
202
229
|
if usage.rebound:
|
|
203
|
-
return
|
|
230
|
+
return "rebound"
|
|
204
231
|
if usage.loaded:
|
|
205
|
-
return
|
|
206
|
-
|
|
232
|
+
return "loaded"
|
|
233
|
+
if isinstance(statement, (ast.Break, ast.Continue, ast.Raise, ast.Return)):
|
|
234
|
+
return "terminal"
|
|
235
|
+
return "none"
|
|
207
236
|
|
|
208
237
|
|
|
209
238
|
@final
|
|
@@ -234,6 +263,10 @@ class _NameUsage(ast.NodeVisitor):
|
|
|
234
263
|
def visit_Lambda(self, node: ast.Lambda) -> None:
|
|
235
264
|
_ = node
|
|
236
265
|
|
|
266
|
+
@override
|
|
267
|
+
def visit_GeneratorExp(self, node: ast.GeneratorExp) -> None:
|
|
268
|
+
_ = node
|
|
269
|
+
|
|
237
270
|
def visit_Import(self, node: ast.Import) -> None:
|
|
238
271
|
self.rebound |= any((alias.asname or alias.name.split(".", maxsplit=1)[0]) == self.name for alias in node.names)
|
|
239
272
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/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.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_ast_index.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_comments.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_docstrings.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_first_party.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/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.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_project_index.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_prose_budget.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/_registry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/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.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/mock_without_spec.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/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.81.8 → sarj_python_lint-0.81.11}/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.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/no_secret_in_log.py
RENAMED
|
File without changes
|
{sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/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.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/over_mocked_test.py
RENAMED
|
File without changes
|
|
File without changes
|
{sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/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.8 → sarj_python_lint-0.81.11}/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.8 → sarj_python_lint-0.81.11}/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
|
{sarj_python_lint-0.81.8 → sarj_python_lint-0.81.11}/src/sarj_python_lint/rules/unused_mock_setup.py
RENAMED
|
File without changes
|