sarj-python-lint 1.7.1__tar.gz → 1.7.3__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 (159) hide show
  1. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/PKG-INFO +1 -1
  2. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/pyproject.toml +1 -1
  3. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +28 -7
  4. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +8 -8
  5. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +30 -0
  6. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +58 -1
  7. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +1 -1
  8. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/.gitignore +0 -0
  9. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/LICENSE +0 -0
  10. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/README.md +0 -0
  11. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/__init__.py +0 -0
  12. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/__main__.py +0 -0
  13. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/_analysis_session.py +0 -0
  14. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/_file_context.py +0 -0
  15. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/_filesystem.py +0 -0
  16. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/_python_target.py +0 -0
  17. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/_ratchet_cli.py +0 -0
  18. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/_secret_names.py +0 -0
  19. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/_version.py +0 -0
  20. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/benchmark.py +0 -0
  21. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/json_boundary.py +0 -0
  22. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/py.typed +0 -0
  23. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/ratchet.py +0 -0
  24. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rule_base.py +0 -0
  25. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/__init__.py +0 -0
  26. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
  27. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_ast_index.py +0 -0
  28. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_ast_position.py +0 -0
  29. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_comments.py +0 -0
  30. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
  31. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_docstrings.py +0 -0
  32. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_fastapi.py +0 -0
  33. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_first_party.py +0 -0
  34. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
  35. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_imports.py +0 -0
  36. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_logging.py +0 -0
  37. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
  38. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_paths.py +0 -0
  39. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_project_index.py +0 -0
  40. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
  41. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_pytest.py +0 -0
  42. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_registry.py +0 -0
  43. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_resource_provenance.py +0 -0
  44. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_set_fields.py +0 -0
  45. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_sql.py +0 -0
  46. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
  47. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
  48. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/_test_composition.py +0 -0
  49. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
  50. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
  51. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
  52. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
  53. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
  54. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
  55. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
  56. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
  57. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
  58. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
  59. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
  60. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
  61. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
  62. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
  63. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
  64. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
  65. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
  66. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
  67. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
  68. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
  69. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
  70. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
  71. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
  72. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
  73. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
  74. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
  75. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
  76. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
  77. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
  78. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
  79. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
  80. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
  81. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
  82. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
  83. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
  84. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
  85. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
  86. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
  87. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
  88. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
  89. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
  90. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
  91. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
  92. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
  93. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
  94. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
  95. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_select_star.py +0 -0
  96. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
  97. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
  98. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
  99. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
  100. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
  101. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
  102. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
  103. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
  104. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
  105. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
  106. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
  107. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
  108. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
  109. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
  110. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
  111. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
  112. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
  113. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
  114. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
  115. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
  116. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
  117. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
  118. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
  119. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
  120. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
  121. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
  122. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
  123. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
  124. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
  125. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
  126. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
  127. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
  128. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
  129. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
  130. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
  131. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
  132. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
  133. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
  134. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
  135. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
  136. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
  137. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
  138. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
  139. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
  140. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
  141. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
  142. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
  143. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
  144. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
  145. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
  146. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
  147. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
  148. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
  149. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
  150. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/stepdown.py +0 -0
  151. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
  152. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
  153. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
  154. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
  155. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
  156. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
  157. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
  158. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
  159. {sarj_python_lint-1.7.1 → sarj_python_lint-1.7.3}/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.7.1
3
+ Version: 1.7.3
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.7.1"
3
+ version = "1.7.3"
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" }]
@@ -17,7 +17,6 @@ from sarj_python_lint.rule_base import (
17
17
  Severity,
18
18
  is_suppressed,
19
19
  )
20
- from sarj_python_lint.rules._ast_index import walk as walk_ast
21
20
  from sarj_python_lint.rules._paths import is_test_path, is_test_support_path
22
21
 
23
22
 
@@ -66,7 +65,7 @@ class NoServiceBehaviorInSettings(Rule):
66
65
  autofix=AutofixPolicy.NONE,
67
66
  limitations=(
68
67
  "Only direct module classes named *Settings, *Config, *Configuration, or *Options are checked.",
69
- "A finding requires a typed collaborator field and an ordinary public method that calls that field; pure computations, properties, validators, serializers, factories, generated files, and tests are excluded.",
68
+ "A finding requires a typed collaborator field and an ordinary public method that calls that field; pure computations, properties, validators, serializers, factories, generated files, and tests are excluded. Nested scopes, containers of collaborators, class objects, and methods that rebind self are not inferred.",
70
69
  "The rule does not require every data object to use a particular record library and does not infer behavior from a filename alone.",
71
70
  ),
72
71
  examples=(
@@ -180,10 +179,19 @@ def _collaborator_fields(node: ast.ClassDef) -> frozenset[str]:
180
179
  def _is_collaborator_annotation(annotation: ast.expr | None) -> bool:
181
180
  if annotation is None:
182
181
  return False
183
- return any(
184
- _COLLABORATOR_RE.search(_dotted_tail(candidate) or "") is not None
185
- for candidate in walk_ast(annotation)
186
- if isinstance(candidate, (ast.Name, ast.Attribute))
182
+ if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
183
+ return _is_collaborator_annotation(annotation.left) or _is_collaborator_annotation(annotation.right)
184
+ if isinstance(annotation, ast.Subscript):
185
+ wrapper = _dotted_tail(annotation.value)
186
+ if wrapper in {"Optional", "Union"}:
187
+ members = annotation.slice.elts if isinstance(annotation.slice, ast.Tuple) else [annotation.slice]
188
+ return any(_is_collaborator_annotation(member) for member in members)
189
+ if wrapper == "Annotated" and isinstance(annotation.slice, ast.Tuple) and annotation.slice.elts:
190
+ return _is_collaborator_annotation(annotation.slice.elts[0])
191
+ return _is_collaborator_annotation(annotation.value)
192
+ return (
193
+ isinstance(annotation, (ast.Name, ast.Attribute))
194
+ and _COLLABORATOR_RE.search(_dotted_tail(annotation) or "") is not None
187
195
  )
188
196
 
189
197
 
@@ -198,9 +206,22 @@ def _is_behavioral_method(method: ast.FunctionDef | ast.AsyncFunctionDef) -> boo
198
206
 
199
207
 
200
208
  def _calls_collaborator(method: ast.FunctionDef | ast.AsyncFunctionDef, fields: frozenset[str]) -> bool:
209
+ nodes: list[ast.AST] = []
210
+ pending: list[ast.AST] = list(method.body)
211
+ while pending:
212
+ node = pending.pop()
213
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef, ast.Lambda)):
214
+ continue
215
+ nodes.append(node)
216
+ pending.extend(ast.iter_child_nodes(node))
217
+ if any(
218
+ isinstance(node, ast.Name) and node.id == "self" and isinstance(node.ctx, (ast.Store, ast.Del))
219
+ for node in nodes
220
+ ):
221
+ return False
201
222
  return any(
202
223
  (field := _self_field(call.func)) is not None and field in fields
203
- for call in walk_ast(method)
224
+ for call in nodes
204
225
  if isinstance(call, ast.Call)
205
226
  )
206
227
 
@@ -219,14 +219,14 @@ def _is_syntax_required(owner: ast.Module | ast.ClassDef | ast.FunctionDef | ast
219
219
  def _owner_keys(tree: ast.Module) -> dict[int, str]:
220
220
  keys = {id(tree): "__doc__"}
221
221
 
222
- def visit(body: list[ast.stmt], prefix: tuple[str, ...]) -> None:
223
- for statement in body:
224
- if isinstance(statement, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
225
- qualified = (*prefix, statement.name)
226
- keys[id(statement)] = ".".join(qualified)
227
- visit(statement.body, qualified)
228
-
229
- visit(tree.body, ())
222
+ def visit(node: ast.AST, prefix: tuple[str, ...]) -> None:
223
+ if isinstance(node, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
224
+ prefix = (*prefix, node.name)
225
+ keys[id(node)] = ".".join(prefix)
226
+ for child in ast.iter_child_nodes(node):
227
+ visit(child, prefix)
228
+
229
+ visit(tree, ())
230
230
  return keys
231
231
 
232
232
 
@@ -63,6 +63,36 @@ class PreferInjectedDependencyOverMonkeypatch(Rule):
63
63
  "A nested function that captures a monkeypatch handle from an outer scope is not inferred.",
64
64
  ),
65
65
  examples=(
66
+ RuleExample(
67
+ example_id="patched-constructor-failure",
68
+ title="Avoid replacing the SDK constructor lookup",
69
+ outcome=ExampleOutcome.MATCH,
70
+ files=(
71
+ ExampleFile.python(
72
+ "tests/test_adapter.py",
73
+ "import pytest\nfrom unittest.mock import patch\ndef test_constructor_failure():\n with patch('transport.Client', side_effect=ConnectionError):\n with pytest.raises(AdapterUnavailable):\n open_adapter()\n",
74
+ ),
75
+ ),
76
+ focus_path=PurePosixPath("tests/test_adapter.py"),
77
+ expected_count=1,
78
+ public=True,
79
+ scenario="constructor-failure",
80
+ ),
81
+ RuleExample(
82
+ example_id="injected-constructor-failure",
83
+ title="Inject a factory to exercise constructor failure",
84
+ outcome=ExampleOutcome.NO_MATCH,
85
+ files=(
86
+ ExampleFile.python(
87
+ "tests/test_adapter.py",
88
+ "import pytest\ndef unavailable_client():\n raise ConnectionError\ndef test_constructor_failure():\n with pytest.raises(AdapterUnavailable):\n open_adapter(client_factory=unavailable_client)\n",
89
+ ),
90
+ ),
91
+ focus_path=PurePosixPath("tests/test_adapter.py"),
92
+ expected_count=0,
93
+ public=True,
94
+ scenario="constructor-failure",
95
+ ),
66
96
  RuleExample(
67
97
  example_id="patched-first-party-collaborator",
68
98
  title="Test replaces an application collaborator",
@@ -60,12 +60,43 @@ class PreferMonkeypatchForProcessStateInTest(Rule):
60
60
  category=RuleCategory.TESTING,
61
61
  autofix=AutofixPolicy.NONE,
62
62
  limitations=(
63
+ "Teardown after a single direct yield in a module-level, import-proven pytest or pytest-asyncio fixture is excluded: registering undo operations during cleanup can restore the temporary state. Nested fixtures, conditional/delegated yields and out-of-line cleanup ownership are not inferred.",
63
64
  "Only maintained test paths are analyzed; generated files, production code, and module/class bootstrap mutations are excluded.",
64
65
  "The rule covers os.chdir, sys.path.insert(0, ...), and direct sys.modules set, delete, or unused pop operations with an equivalent pytest restoring helper.",
65
66
  "Environment variables remain owned by Ruff TID251/B003; argv, locale, timezone, warning filters, and process APIs without an equivalent helper are outside this rule.",
66
67
  "Imports and import aliases are resolved conservatively. Indirect container aliases and dynamically selected modules are not inferred.",
67
68
  ),
68
69
  examples=(
70
+ RuleExample(
71
+ example_id="fixture-cleanup-unscoped",
72
+ title="Restore setup mutations even when setup fails",
73
+ outcome=ExampleOutcome.MATCH,
74
+ files=(
75
+ ExampleFile.python(
76
+ "tests/test_plugins.py",
77
+ "import sys\nimport pytest\n@pytest.fixture\ndef fresh_plugin():\n sys.modules.pop('temporary_plugin', None)\n yield\n",
78
+ ),
79
+ ),
80
+ focus_path=PurePosixPath("tests/test_plugins.py"),
81
+ expected_count=1,
82
+ public=True,
83
+ scenario="fixture-cleanup",
84
+ ),
85
+ RuleExample(
86
+ example_id="yield-fixture-cleanup",
87
+ title="Do not undo fixture teardown",
88
+ outcome=ExampleOutcome.NO_MATCH,
89
+ files=(
90
+ ExampleFile.python(
91
+ "tests/test_plugins.py",
92
+ "import sys\nimport pytest\n@pytest.fixture\ndef fresh_plugin():\n yield\n sys.modules.pop('temporary_plugin', None)\n",
93
+ ),
94
+ ),
95
+ focus_path=PurePosixPath("tests/test_plugins.py"),
96
+ expected_count=0,
97
+ public=True,
98
+ scenario="fixture-cleanup",
99
+ ),
69
100
  RuleExample(
70
101
  example_id="direct-module-registry-mutation",
71
102
  title="Test installs a module without guaranteed restoration",
@@ -112,7 +143,7 @@ class PreferMonkeypatchForProcessStateInTest(Rule):
112
143
  mutations = [
113
144
  mutation
114
145
  for function in _functions(tree, node_index=context.node_index)
115
- for mutation in _function_mutations(function, module_imports)
146
+ for mutation in _function_mutations(function, module_imports, module_level=function in tree.body)
116
147
  ]
117
148
  diagnostics = [
118
149
  Diagnostic(
@@ -143,6 +174,8 @@ def _functions(
143
174
  def _function_mutations(
144
175
  function: ast.FunctionDef | ast.AsyncFunctionDef,
145
176
  module_imports: ImportIndex,
177
+ *,
178
+ module_level: bool,
146
179
  ) -> list[_Mutation]:
147
180
  local_tree = ast.Module(body=function.body, type_ignores=[])
148
181
  local_imports = ImportIndex.from_tree(local_tree)
@@ -155,9 +188,33 @@ def _function_mutations(
155
188
  continue
156
189
  mutations.append(mutation)
157
190
  restored = _restored_mutations(nodes, mutations, module_imports, local_imports)
191
+ if module_level:
192
+ restored.update(_fixture_teardown_nodes(function, nodes, module_imports))
158
193
  return [mutation for mutation in mutations if mutation.node not in restored]
159
194
 
160
195
 
196
+ def _fixture_teardown_nodes(
197
+ function: ast.FunctionDef | ast.AsyncFunctionDef,
198
+ nodes: tuple[ast.AST, ...],
199
+ module_imports: ImportIndex,
200
+ ) -> set[ast.AST]:
201
+ if not any(
202
+ module_imports.resolves(
203
+ decorator.func if isinstance(decorator, ast.Call) else decorator,
204
+ sources=frozenset({"pytest", "pytest_asyncio"}),
205
+ symbol="fixture",
206
+ )
207
+ for decorator in function.decorator_list
208
+ ):
209
+ return set()
210
+ if sum(isinstance(node, ast.Yield | ast.YieldFrom) for node in nodes) != 1:
211
+ return set()
212
+ for index, statement in enumerate(function.body):
213
+ if isinstance(statement, ast.Expr) and isinstance(statement.value, ast.Yield):
214
+ return set(_descendants(function.body[index + 1 :]))
215
+ return set()
216
+
217
+
161
218
  def _lexical_nodes(function: ast.FunctionDef | ast.AsyncFunctionDef) -> Iterator[ast.AST]:
162
219
  stack: list[ast.AST] = [node for node in reversed(function.body) if not isinstance(node, _SCOPE_NODES)]
163
220
  while stack:
@@ -55,7 +55,7 @@ class RequireExplicitPsycopgTransaction(Rule):
55
55
  "Inspects literal SQL, simple local/module constants, and psycopg.sql.SQL wrappers. Dynamic SQL and unsupported SQL syntax are excluded.",
56
56
  "A locking SELECT followed by a write on the same connection outside transaction() is reported; a single statement containing a mutation is treated as atomic. Multiple writes on a reachable local path are a warning because independent writes can be intentional.",
57
57
  "Loops, exception handlers, match statements, comprehensions, and nested closures are not analyzed as execution paths. Manual BEGIN/commit protocols are not inferred. Tests, support fixtures, and generated files are excluded.",
58
- "The rule cannot prove isolation level, database constraints, or application-level atomicity; fault-injection integration tests remain necessary.",
58
+ "Non-autocommit connections can already provide implicit transaction protection. A manual commit is not itself evidence of a defect or of safety in autocommit mode; document the proven connection configuration when retaining that protocol. The rule cannot prove isolation level, database constraints, or application-level atomicity; fault-injection integration tests remain necessary.",
59
59
  ),
60
60
  examples=(
61
61
  RuleExample(