sarj-python-lint 1.8.2__tar.gz → 1.8.4__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-1.8.2 → sarj_python_lint-1.8.4}/PKG-INFO +1 -1
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/pyproject.toml +1 -1
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_project_index.py +177 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/iac_source_coupled_test.py +10 -3
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +191 -45
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/require_port_for_service.py +339 -1
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/.gitignore +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/LICENSE +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/README.md +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/_analysis_session.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/_file_context.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/_filesystem.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/_python_target.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/benchmark.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/json_boundary.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_ast_position.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_comments.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_docstrings.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_fastapi.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_imports.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_registry.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_resource_provenance.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_set_fields.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_test_composition.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/discourage_nullable_constructor_parameters.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
- {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/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: 1.8.
|
|
3
|
+
Version: 1.8.4
|
|
4
4
|
Summary: Custom Python lint rules — AST-based, pre-commit-friendly, hypermodern defaults
|
|
5
5
|
Project-URL: Homepage, https://code-standards.sarj.ai/rules/python/
|
|
6
6
|
Project-URL: Documentation, https://code-standards.sarj.ai/rules/python/
|
{sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_project_index.py
RENAMED
|
@@ -4,6 +4,7 @@ import ast
|
|
|
4
4
|
from collections.abc import Mapping
|
|
5
5
|
from contextlib import suppress
|
|
6
6
|
from dataclasses import dataclass
|
|
7
|
+
from functools import cached_property
|
|
7
8
|
import os
|
|
8
9
|
from pathlib import Path
|
|
9
10
|
import re
|
|
@@ -12,6 +13,7 @@ from typing import TYPE_CHECKING, final
|
|
|
12
13
|
|
|
13
14
|
from sarj_python_lint.rules._ast_index import walk as walk_ast
|
|
14
15
|
from sarj_python_lint.rules._first_party import FirstPartyFacts, project_root
|
|
16
|
+
from sarj_python_lint.rules._paths import is_generated
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
if TYPE_CHECKING:
|
|
@@ -179,6 +181,27 @@ class ProjectIndexSet:
|
|
|
179
181
|
consumers.update(_unit_constructor_consumers(candidate, candidate.tree, target))
|
|
180
182
|
return frozenset(consumers)
|
|
181
183
|
|
|
184
|
+
def constructor_operations(self, unit: SourceUnit, name: str) -> frozenset[tuple[Path, str]]:
|
|
185
|
+
if unit.module is None:
|
|
186
|
+
return frozenset()
|
|
187
|
+
return self._constructor_operations.get(SymbolRef(unit.module, name), frozenset())
|
|
188
|
+
|
|
189
|
+
@cached_property
|
|
190
|
+
def _constructor_operations(self) -> Mapping[SymbolRef, frozenset[tuple[Path, str]]]:
|
|
191
|
+
module_counts: dict[str, int] = {}
|
|
192
|
+
for unit in self._units.values():
|
|
193
|
+
if unit.module is not None:
|
|
194
|
+
module_counts[unit.module] = module_counts.get(unit.module, 0) + 1
|
|
195
|
+
owned_modules = frozenset(module for module, count in module_counts.items() if count == 1)
|
|
196
|
+
operations: dict[SymbolRef, set[tuple[Path, str]]] = {}
|
|
197
|
+
for unit in self._units.values():
|
|
198
|
+
if unit.tree is None or unit.module not in owned_modules or is_generated(unit.path, unit.source):
|
|
199
|
+
continue
|
|
200
|
+
for symbol, operation in _unit_constructor_operations(unit, unit.tree, owned_modules):
|
|
201
|
+
if symbol.module in owned_modules and symbol in self._classes:
|
|
202
|
+
operations.setdefault(symbol, set()).add((unit.path, operation))
|
|
203
|
+
return MappingProxyType({symbol: frozenset(calls) for symbol, calls in operations.items()})
|
|
204
|
+
|
|
182
205
|
def direct_subclasses(self, unit: SourceUnit, name: str) -> frozenset[tuple[Path, str]]:
|
|
183
206
|
if unit.module is None:
|
|
184
207
|
return frozenset()
|
|
@@ -554,6 +577,160 @@ def _unit_constructor_consumers(
|
|
|
554
577
|
yield (candidate.path, owner.name)
|
|
555
578
|
|
|
556
579
|
|
|
580
|
+
def _unit_constructor_operations(
|
|
581
|
+
unit: SourceUnit, tree: ast.Module, owned_modules: frozenset[str]
|
|
582
|
+
) -> Iterator[tuple[SymbolRef, str]]:
|
|
583
|
+
if any(
|
|
584
|
+
isinstance(item, ast.ImportFrom) and any(alias.name == "*" for alias in item.names) for item in walk_ast(tree)
|
|
585
|
+
):
|
|
586
|
+
return
|
|
587
|
+
rebound = _module_constructor_rebindings(tree)
|
|
588
|
+
for owner in tree.body:
|
|
589
|
+
self_symbol = SymbolRef(unit.module or "", owner.name) if isinstance(owner, ast.ClassDef) else None
|
|
590
|
+
for method in _direct_functions(owner):
|
|
591
|
+
for symbol, operation in _function_constructor_operations(unit, method, rebound, owned_modules):
|
|
592
|
+
if symbol != self_symbol:
|
|
593
|
+
yield symbol, operation
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
def _module_constructor_rebindings(tree: ast.Module) -> frozenset[str]:
|
|
597
|
+
names: set[str] = set()
|
|
598
|
+
declarations: dict[str, int] = {}
|
|
599
|
+
for statement in tree.body:
|
|
600
|
+
for name in _module_constructor_declarations(statement):
|
|
601
|
+
declarations[name] = declarations.get(name, 0) + 1
|
|
602
|
+
if isinstance(statement, ast.FunctionDef | ast.AsyncFunctionDef):
|
|
603
|
+
names.add(statement.name)
|
|
604
|
+
elif not isinstance(statement, ast.ClassDef | ast.Import | ast.ImportFrom):
|
|
605
|
+
names.update(_bound_constructor_names(statement))
|
|
606
|
+
names.update(name for name, count in declarations.items() if count > 1)
|
|
607
|
+
return frozenset(names)
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
def _module_constructor_declarations(statement: ast.stmt) -> frozenset[str]:
|
|
611
|
+
if isinstance(statement, ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef):
|
|
612
|
+
return frozenset({statement.name})
|
|
613
|
+
return _bound_constructor_names(statement)
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
def _direct_functions(owner: ast.stmt) -> tuple[ast.FunctionDef | ast.AsyncFunctionDef, ...]:
|
|
617
|
+
if isinstance(owner, ast.FunctionDef | ast.AsyncFunctionDef):
|
|
618
|
+
return (owner,)
|
|
619
|
+
if isinstance(owner, ast.ClassDef):
|
|
620
|
+
return tuple(item for item in owner.body if isinstance(item, ast.FunctionDef | ast.AsyncFunctionDef))
|
|
621
|
+
return ()
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
def _function_constructor_operations(
|
|
625
|
+
unit: SourceUnit,
|
|
626
|
+
method: ast.FunctionDef | ast.AsyncFunctionDef,
|
|
627
|
+
rebound: frozenset[str],
|
|
628
|
+
owned_modules: frozenset[str],
|
|
629
|
+
) -> Iterator[tuple[SymbolRef, str]]:
|
|
630
|
+
shadowed = _bound_constructor_names(method) | rebound
|
|
631
|
+
for statement in method.body:
|
|
632
|
+
if isinstance(statement, ast.Expr | ast.Assign | ast.AnnAssign | ast.Return):
|
|
633
|
+
operation = _direct_constructor_operation(unit, statement.value, shadowed, owned_modules)
|
|
634
|
+
if operation is not None:
|
|
635
|
+
yield operation
|
|
636
|
+
if isinstance(
|
|
637
|
+
statement,
|
|
638
|
+
ast.Return
|
|
639
|
+
| ast.Raise
|
|
640
|
+
| ast.If
|
|
641
|
+
| ast.For
|
|
642
|
+
| ast.AsyncFor
|
|
643
|
+
| ast.While
|
|
644
|
+
| ast.Try
|
|
645
|
+
| ast.TryStar
|
|
646
|
+
| ast.With
|
|
647
|
+
| ast.AsyncWith
|
|
648
|
+
| ast.Match,
|
|
649
|
+
):
|
|
650
|
+
break
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
def _direct_constructor_operation(
|
|
654
|
+
unit: SourceUnit, value: ast.expr | None, shadowed: frozenset[str], owned_modules: frozenset[str]
|
|
655
|
+
) -> tuple[SymbolRef, str] | None:
|
|
656
|
+
if isinstance(value, ast.Await):
|
|
657
|
+
value = value.value
|
|
658
|
+
if not isinstance(value, ast.Call) or not isinstance(value.func, ast.Attribute):
|
|
659
|
+
return None
|
|
660
|
+
if not isinstance(value.func.value, ast.Call):
|
|
661
|
+
return None
|
|
662
|
+
constructor = value.func.value.func
|
|
663
|
+
root = constructor
|
|
664
|
+
while isinstance(root, ast.Attribute):
|
|
665
|
+
root = root.value
|
|
666
|
+
if not isinstance(root, ast.Name) or root.id in shadowed:
|
|
667
|
+
return None
|
|
668
|
+
symbol = _owned_constructor_symbol(unit, constructor, owned_modules)
|
|
669
|
+
return (symbol, value.func.attr) if symbol is not None else None
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
def _owned_constructor_symbol(
|
|
673
|
+
unit: SourceUnit, expression: ast.expr, owned_modules: frozenset[str]
|
|
674
|
+
) -> SymbolRef | None:
|
|
675
|
+
if isinstance(expression, ast.Name):
|
|
676
|
+
return _resolve(unit, expression)
|
|
677
|
+
parts: list[str] = []
|
|
678
|
+
current = expression
|
|
679
|
+
while isinstance(current, ast.Attribute):
|
|
680
|
+
parts.append(current.attr)
|
|
681
|
+
current = current.value
|
|
682
|
+
if not isinstance(current, ast.Name) or not parts:
|
|
683
|
+
return None
|
|
684
|
+
imported = unit.imports.get(current.id)
|
|
685
|
+
if imported is None or imported.name:
|
|
686
|
+
return None
|
|
687
|
+
ordered = list(reversed(parts))
|
|
688
|
+
module = ".".join((imported.module, *ordered[:-1]))
|
|
689
|
+
if len(ordered) > 1 and not _explicit_constructor_module(unit, module):
|
|
690
|
+
return None
|
|
691
|
+
return SymbolRef(module, ordered[-1]) if module in owned_modules else None
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
def _explicit_constructor_module(unit: SourceUnit, module: str) -> bool:
|
|
695
|
+
if unit.tree is None:
|
|
696
|
+
return False
|
|
697
|
+
return any(
|
|
698
|
+
alias.name == module and alias.asname is None
|
|
699
|
+
for statement in _module_import_statements(unit.tree)
|
|
700
|
+
if isinstance(statement, ast.Import)
|
|
701
|
+
for alias in statement.names
|
|
702
|
+
)
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
def _bound_constructor_names(node: ast.AST) -> frozenset[str]:
|
|
706
|
+
names: set[str] = set()
|
|
707
|
+
for item in walk_ast(node):
|
|
708
|
+
match item:
|
|
709
|
+
case (
|
|
710
|
+
ast.Name(id=name, ctx=(ast.Store() | ast.Del()))
|
|
711
|
+
| ast.arg(arg=name)
|
|
712
|
+
| ast.ClassDef(name=name)
|
|
713
|
+
| ast.FunctionDef(name=name)
|
|
714
|
+
| ast.AsyncFunctionDef(name=name)
|
|
715
|
+
| ast.ExceptHandler(name=str(name))
|
|
716
|
+
| ast.MatchAs(name=str(name))
|
|
717
|
+
| ast.MatchStar(name=str(name))
|
|
718
|
+
| ast.MatchMapping(rest=str(name))
|
|
719
|
+
):
|
|
720
|
+
names.add(name)
|
|
721
|
+
case ast.Import(names=aliases) | ast.ImportFrom(names=aliases):
|
|
722
|
+
names.update(alias.asname or alias.name.split(".", 1)[0] for alias in aliases)
|
|
723
|
+
case ast.Attribute(ctx=(ast.Store() | ast.Del())):
|
|
724
|
+
root = item.value
|
|
725
|
+
while isinstance(root, ast.Attribute):
|
|
726
|
+
root = root.value
|
|
727
|
+
if isinstance(root, ast.Name):
|
|
728
|
+
names.add(root.id)
|
|
729
|
+
case _:
|
|
730
|
+
pass
|
|
731
|
+
return frozenset(names)
|
|
732
|
+
|
|
733
|
+
|
|
557
734
|
def _class_has_typed_dependency(unit: SourceUnit, owner: ast.ClassDef, target: SymbolRef) -> bool:
|
|
558
735
|
for statement in owner.body:
|
|
559
736
|
if isinstance(statement, ast.AnnAssign) and _annotation_contains_symbol(unit, statement.annotation, target):
|
|
@@ -15,14 +15,19 @@ from sarj_python_lint.rule_base import (
|
|
|
15
15
|
Severity,
|
|
16
16
|
)
|
|
17
17
|
from sarj_python_lint.rules._paths import is_test_path
|
|
18
|
-
from sarj_python_lint.rules.no_raw_source_text_test_oracle import
|
|
18
|
+
from sarj_python_lint.rules.no_raw_source_text_test_oracle import (
|
|
19
|
+
IAC_JSON_SOURCE_SUFFIXES,
|
|
20
|
+
FunctionAnalyzer,
|
|
21
|
+
module_source_path_tuples,
|
|
22
|
+
top_level_test_functions,
|
|
23
|
+
)
|
|
19
24
|
|
|
20
25
|
|
|
21
26
|
if TYPE_CHECKING:
|
|
22
27
|
from sarj_python_lint._file_context import PythonFileContext
|
|
23
28
|
|
|
24
29
|
|
|
25
|
-
IAC_SOURCE_SUFFIXES = (".hcl", ".tf", ".
|
|
30
|
+
IAC_SOURCE_SUFFIXES = (".hcl", ".tf", ".tfvars", ".tftest.hcl", *IAC_JSON_SOURCE_SUFFIXES)
|
|
26
31
|
|
|
27
32
|
|
|
28
33
|
@final
|
|
@@ -42,7 +47,7 @@ class IacSourceCoupledTest(Rule):
|
|
|
42
47
|
),
|
|
43
48
|
category=RuleCategory.TESTING,
|
|
44
49
|
limitations=(
|
|
45
|
-
"The rule follows local aliases, path collections, context-managed reads, and
|
|
50
|
+
"The rule follows local aliases, path collections, context-managed reads, common normalization, and direct comprehension reads unpacked from stable module-level path tuples; interprocedural flows remain unreported.",
|
|
46
51
|
"Files produced beneath recognized temporary-directory fixtures are generated outputs and remain unreported.",
|
|
47
52
|
"The Python detector currently owns Terraform and HCL suffixes; YAML remains with the general source-coupled rule.",
|
|
48
53
|
"Fixture, golden, and snapshot paths are treated as deliberate representation contracts; other packaging, formatter, and compatibility contracts require an exact suppression.",
|
|
@@ -93,11 +98,13 @@ class IacSourceCoupledTest(Rule):
|
|
|
93
98
|
return []
|
|
94
99
|
imports = context.module_imports
|
|
95
100
|
source_lines = context.source_lines
|
|
101
|
+
module_tuples = module_source_path_tuples(tree, imports, IAC_SOURCE_SUFFIXES)
|
|
96
102
|
assertions = [
|
|
97
103
|
assertion
|
|
98
104
|
for function, unittest_style in top_level_test_functions(tree, imports)
|
|
99
105
|
for assertion in FunctionAnalyzer(
|
|
100
106
|
IAC_SOURCE_SUFFIXES,
|
|
107
|
+
module_path_tuples=module_tuples,
|
|
101
108
|
imports=imports,
|
|
102
109
|
suppression_code=self.code,
|
|
103
110
|
suppression_lines=source_lines,
|