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.
Files changed (164) hide show
  1. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/PKG-INFO +1 -1
  2. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/pyproject.toml +1 -1
  3. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_project_index.py +177 -0
  4. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/iac_source_coupled_test.py +10 -3
  5. {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
  6. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/require_port_for_service.py +339 -1
  7. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/.gitignore +0 -0
  8. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/LICENSE +0 -0
  9. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/README.md +0 -0
  10. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/__init__.py +0 -0
  11. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/__main__.py +0 -0
  12. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/_analysis_session.py +0 -0
  13. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/_file_context.py +0 -0
  14. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/_filesystem.py +0 -0
  15. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/_python_target.py +0 -0
  16. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/_ratchet_cli.py +0 -0
  17. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/_secret_names.py +0 -0
  18. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/_version.py +0 -0
  19. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/benchmark.py +0 -0
  20. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/json_boundary.py +0 -0
  21. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/py.typed +0 -0
  22. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/ratchet.py +0 -0
  23. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rule_base.py +0 -0
  24. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/__init__.py +0 -0
  25. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
  26. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_ast_index.py +0 -0
  27. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_ast_position.py +0 -0
  28. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_comments.py +0 -0
  29. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
  30. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
  31. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_docstrings.py +0 -0
  32. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_fastapi.py +0 -0
  33. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_first_party.py +0 -0
  34. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
  35. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_imports.py +0 -0
  36. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_logging.py +0 -0
  37. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
  38. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_paths.py +0 -0
  39. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
  40. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_pytest.py +0 -0
  41. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_registry.py +0 -0
  42. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_resource_provenance.py +0 -0
  43. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_set_fields.py +0 -0
  44. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_sql.py +0 -0
  45. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
  46. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
  47. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/_test_composition.py +0 -0
  48. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
  49. {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
  50. {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
  51. {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
  52. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/discourage_nullable_constructor_parameters.py +0 -0
  53. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
  54. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
  55. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
  56. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
  57. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
  58. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
  59. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
  60. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
  61. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
  62. {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
  63. {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
  64. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
  65. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
  66. {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
  67. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
  68. {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
  69. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
  70. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
  71. {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
  72. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
  73. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
  74. {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
  75. {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
  76. {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
  77. {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
  78. {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
  79. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
  80. {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
  81. {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
  82. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
  83. {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
  84. {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
  85. {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
  86. {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
  87. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
  88. {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
  89. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
  90. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
  91. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
  92. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
  93. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_select_star.py +0 -0
  94. {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
  95. {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
  96. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
  97. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
  98. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
  99. {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
  100. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
  101. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
  102. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
  103. {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
  104. {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
  105. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
  106. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
  107. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
  108. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
  109. {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
  110. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
  111. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
  112. {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
  113. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
  114. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
  115. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
  116. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
  117. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
  118. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
  119. {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
  120. {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
  121. {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
  122. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
  123. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
  124. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
  125. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
  126. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
  127. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
  128. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
  129. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
  130. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
  131. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
  132. {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
  133. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
  134. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
  135. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
  136. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
  137. {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
  138. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
  139. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
  140. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
  141. {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
  142. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
  143. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
  144. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
  145. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
  146. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
  147. {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
  148. {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
  149. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
  150. {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
  151. {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
  152. {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
  153. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
  154. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
  155. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/stepdown.py +0 -0
  156. {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
  157. {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
  158. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
  159. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
  160. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
  161. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
  162. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
  163. {sarj_python_lint-1.8.2 → sarj_python_lint-1.8.4}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
  164. {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.2
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/
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sarj-python-lint"
3
- version = "1.8.2"
3
+ version = "1.8.4"
4
4
  description = "Custom Python lint rules — AST-based, pre-commit-friendly, hypermodern defaults"
5
5
  readme = "README.md"
6
6
  authors = [{ name = "sarj-ai" }]
@@ -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 FunctionAnalyzer, top_level_test_functions
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", ".tf.json", ".tfvars", ".tftest.hcl", ".tftest.json")
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 common normalization; interprocedural flows remain unreported.",
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,