sarj-python-lint 1.28.4__tar.gz → 1.28.6__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 (192) hide show
  1. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/PKG-INFO +1 -1
  2. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/pyproject.toml +1 -1
  3. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/_file_context.py +11 -0
  4. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +1 -13
  5. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +4 -1
  6. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +2 -15
  7. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_offset_pagination.py +2 -14
  8. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +5 -2
  9. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +4 -2
  10. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/require_precise_factory_signature.py +45 -14
  11. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/.gitignore +0 -0
  12. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/LICENSE +0 -0
  13. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/README.md +0 -0
  14. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/__init__.py +0 -0
  15. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/__main__.py +0 -0
  16. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/_analysis_session.py +0 -0
  17. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/_filesystem.py +0 -0
  18. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/_python_target.py +0 -0
  19. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/_ratchet_cli.py +0 -0
  20. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/_secret_names.py +0 -0
  21. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/_source.py +0 -0
  22. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/_version.py +0 -0
  23. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/benchmark.py +0 -0
  24. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/interpreter_argv.py +0 -0
  25. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/json_boundary.py +0 -0
  26. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/py.typed +0 -0
  27. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/ratchet.py +0 -0
  28. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rule_base.py +0 -0
  29. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/__init__.py +0 -0
  30. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
  31. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_ast_index.py +0 -0
  32. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_ast_position.py +0 -0
  33. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_closed_helpers.py +0 -0
  34. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_comments.py +0 -0
  35. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
  36. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_copy_on_write.py +0 -0
  37. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
  38. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_docstrings.py +0 -0
  39. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_fastapi.py +0 -0
  40. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_first_party.py +0 -0
  41. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
  42. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_imports.py +0 -0
  43. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_local_source.py +0 -0
  44. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_logging.py +0 -0
  45. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
  46. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_paths.py +0 -0
  47. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_project_index.py +0 -0
  48. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
  49. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_pytest.py +0 -0
  50. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_registry.py +0 -0
  51. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_resource_provenance.py +0 -0
  52. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_set_fields.py +0 -0
  53. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_sql.py +0 -0
  54. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
  55. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
  56. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_test_composition.py +0 -0
  57. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/_test_provenance.py +0 -0
  58. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
  59. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
  60. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
  61. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
  62. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/duplicate_adjacent_exception_handler.py +0 -0
  63. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/duplicate_closed_pure_helper.py +0 -0
  64. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
  65. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
  66. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
  67. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
  68. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
  69. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
  70. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
  71. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
  72. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
  73. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
  74. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_before_validator_input_mutation.py +0 -0
  75. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_broad_keyword_capture.py +0 -0
  76. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
  77. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
  78. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
  79. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
  80. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
  81. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
  82. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
  83. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
  84. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
  85. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
  86. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
  87. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
  88. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
  89. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_fixed_sleep_in_loop.py +0 -0
  90. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
  91. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
  92. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
  93. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
  94. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_input_model_mutation.py +0 -0
  95. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_interpreter_source_arguments.py +0 -0
  96. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
  97. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_mocked_pydantic_value_object.py +0 -0
  98. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
  99. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
  100. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
  101. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
  102. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
  103. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
  104. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
  105. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
  106. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
  107. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
  108. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
  109. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
  110. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
  111. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_select_star.py +0 -0
  112. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
  113. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_shallow_container_type_guard.py +0 -0
  114. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
  115. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
  116. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
  117. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_test_method_grafting.py +0 -0
  118. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
  119. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
  120. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
  121. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_unused_copy_result.py +0 -0
  122. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_unused_underscored_keyword_parameter.py +0 -0
  123. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
  124. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
  125. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
  126. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
  127. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
  128. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
  129. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_autospec_for_callable_mock.py +0 -0
  130. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
  131. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
  132. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
  133. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
  134. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
  135. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
  136. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
  137. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
  138. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
  139. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
  140. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
  141. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
  142. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
  143. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_native_string_check.py +0 -0
  144. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
  145. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
  146. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
  147. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
  148. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_pytest_fixture_injection.py +0 -0
  149. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
  150. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_required_constructor_parameters.py +0 -0
  151. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
  152. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
  153. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
  154. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
  155. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
  156. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
  157. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
  158. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
  159. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
  160. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
  161. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
  162. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
  163. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
  164. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
  165. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/redundant_test_constructor_forwarder.py +0 -0
  166. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
  167. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
  168. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
  169. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/repeated_unimplemented_test_contract.py +0 -0
  170. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
  171. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
  172. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
  173. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/require_injectable_retry_sleep.py +0 -0
  174. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
  175. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
  176. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
  177. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
  178. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
  179. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
  180. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
  181. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
  182. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
  183. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/stepdown.py +0 -0
  184. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
  185. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
  186. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
  187. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
  188. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
  189. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
  190. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
  191. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
  192. {sarj_python_lint-1.28.4 → sarj_python_lint-1.28.6}/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.28.4
3
+ Version: 1.28.6
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.28.4"
3
+ version = "1.28.6"
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" }]
@@ -67,6 +67,17 @@ class PythonFileContext:
67
67
 
68
68
  return consumed_docstring_names(self._valid_tree(), node_index=self.node_index)
69
69
 
70
+ @cached_property
71
+ def docstring_node_ids(self) -> frozenset[int]:
72
+ result: set[int] = set()
73
+ for owner in self.nodes(ast.Module, ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef):
74
+ if not owner.body or not isinstance(owner.body[0], ast.Expr):
75
+ continue
76
+ value = owner.body[0].value
77
+ if isinstance(value, ast.Constant) and isinstance(value.value, str):
78
+ result.add(id(value))
79
+ return frozenset(result)
80
+
70
81
  @property
71
82
  def parents(self) -> Mapping[ast.AST, ast.AST]:
72
83
  return self.node_index.parents
@@ -71,18 +71,6 @@ class _ConstructorImport(NamedTuple):
71
71
  name: str
72
72
 
73
73
 
74
- def _docstring_node_ids(tree: ast.AST, *, node_index: NodeIndex | None = None) -> set[int]:
75
- result: set[int] = set()
76
- for owner in nodes(tree, ast.Module, ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef, index=node_index):
77
- body = owner.body
78
- if not body or not isinstance(body[0], ast.Expr):
79
- continue
80
- value = body[0].value
81
- if isinstance(value, ast.Constant) and isinstance(value.value, str):
82
- result.add(id(value))
83
- return result
84
-
85
-
86
74
  def _is_query_context(node: ast.expr, parents: Mapping[ast.AST, ast.AST], roots: frozenset[int]) -> bool:
87
75
  current: ast.AST = node
88
76
  while True:
@@ -368,7 +356,7 @@ class ComplexPostgresQueryRequiresArchitectureReview(Rule):
368
356
  if not _is_postgres_module(tree, node_index=context.node_index):
369
357
  return []
370
358
 
371
- docstrings = _docstring_node_ids(tree, node_index=context.node_index)
359
+ docstrings = context.docstring_node_ids
372
360
  parents = context.parents
373
361
  query_roots = _query_roots(tree, parents, node_index=context.node_index)
374
362
  diagnostics: list[Diagnostic] = []
@@ -64,13 +64,16 @@ class DefectXfailRequiresExplicitStrict(Rule):
64
64
  ),
65
65
  remediation=(
66
66
  "Set literal `strict=True` so an unexpected pass fails. If only some parameter cases carry the defect, "
67
- "move the marker to those `pytest.param(...)` cases before making it strict."
67
+ "move the marker to those `pytest.param(...)` cases before making it strict. When the defect's exception "
68
+ "type or group shape is independently known, native `raises=...`, where supported, can narrow unrelated failures; "
69
+ "same-type failures still need an origin oracle. Keep intentional broad expected-failure policies."
68
70
  ),
69
71
  category=RuleCategory.TESTING,
70
72
  aliases=("defect-xfail-requires-strict", "xfail-requires-strict"),
71
73
  limitations=(
72
74
  "Only markers resolved through an unambiguous pytest or pytest.mark import are analyzed.",
73
75
  "Only xfail reasons that explicitly identify a defect are analyzed.",
76
+ "Exception narrowing and defect identity remain manual review; strictness or a typed marker alone does not establish failure origin.",
74
77
  "Nondeterministic, recognized Hypothesis, network, environment-gated, run=False, and statically disabled pins are excluded.",
75
78
  "Repository-level strict_xfail / xfail_strict configuration is intentionally not consulted: defect pins remain locally explicit.",
76
79
  "Default pytest test_/Test collection names are recognized; repositories with custom collection patterns may need a local suppression.",
@@ -16,7 +16,7 @@ from sarj_python_lint.rule_base import (
16
16
  RuleExample,
17
17
  Severity,
18
18
  )
19
- from sarj_python_lint.rules._ast_index import nodes, walk
19
+ from sarj_python_lint.rules._ast_index import walk
20
20
  from sarj_python_lint.rules._sql import is_store_module, sql_string_value, strip_sql_noise
21
21
 
22
22
 
@@ -24,7 +24,6 @@ if TYPE_CHECKING:
24
24
  from sqlglot import exp
25
25
 
26
26
  from sarj_python_lint._file_context import PythonFileContext
27
- from sarj_python_lint.rules._ast_index import NodeIndex
28
27
 
29
28
 
30
29
  _QUERY_SHAPE = re.compile(r"\bSELECT\b[\s\S]*?\bFROM\b", re.IGNORECASE)
@@ -65,18 +64,6 @@ _STRONG_ANALYTICAL: tuple[tuple[str, re.Pattern[str]], ...] = (
65
64
  )
66
65
 
67
66
 
68
- def _docstring_node_ids(tree: ast.AST, *, node_index: NodeIndex | None = None) -> set[int]:
69
- result: set[int] = set()
70
- for owner in nodes(tree, ast.Module, ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef, index=node_index):
71
- body = owner.body
72
- if not body or not isinstance(body[0], ast.Expr):
73
- continue
74
- value = body[0].value
75
- if isinstance(value, ast.Constant) and isinstance(value.value, str):
76
- result.add(id(value))
77
- return result
78
-
79
-
80
67
  def analytical_signal(sql: str) -> str | None:
81
68
  if _LOCKING_READ.search(sql) is not None:
82
69
  return None
@@ -217,7 +204,7 @@ class NoAnalyticalAggregationInPostgresStore(Rule):
217
204
  if tree is None:
218
205
  return []
219
206
 
220
- docstrings = _docstring_node_ids(tree, node_index=context.node_index)
207
+ docstrings = context.docstring_node_ids
221
208
  diagnostics: list[Diagnostic] = []
222
209
  consumed: set[int] = set()
223
210
  for node in context.nodes(ast.Constant, ast.BinOp):
@@ -16,13 +16,12 @@ from sarj_python_lint.rule_base import (
16
16
  RuleExample,
17
17
  Severity,
18
18
  )
19
- from sarj_python_lint.rules._ast_index import nodes, walk
19
+ from sarj_python_lint.rules._ast_index import walk
20
20
  from sarj_python_lint.rules._sql import is_store_module, sql_string_value, strip_sql_noise
21
21
 
22
22
 
23
23
  if TYPE_CHECKING:
24
24
  from sarj_python_lint._file_context import PythonFileContext
25
- from sarj_python_lint.rules._ast_index import NodeIndex
26
25
 
27
26
 
28
27
  _DYNAMIC_VALUE = r"(?:%s|%\(\w+\)s|\?\d*|:\w+|@\w+|\$\d+|:__sarj_dynamic__)"
@@ -107,7 +106,7 @@ class NoOffsetPagination(Rule):
107
106
 
108
107
  diags: list[Diagnostic] = []
109
108
  consumed: set[int] = set()
110
- docstrings = _docstring_node_ids(tree, node_index=context.node_index)
109
+ docstrings = context.docstring_node_ids
111
110
  for node in context.nodes(ast.Constant, ast.BinOp, ast.JoinedStr):
112
111
  if id(node) in consumed or id(node) in docstrings:
113
112
  continue
@@ -151,17 +150,6 @@ def _sql_value(node: ast.expr) -> str | None:
151
150
  return sql_string_value(node)
152
151
 
153
152
 
154
- def _docstring_node_ids(tree: ast.Module, *, node_index: NodeIndex | None = None) -> set[int]:
155
- result: set[int] = set()
156
- for owner in nodes(tree, ast.Module, ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef, index=node_index):
157
- if not owner.body or not isinstance(owner.body[0], ast.Expr):
158
- continue
159
- value = owner.body[0].value
160
- if isinstance(value, ast.Constant) and isinstance(value.value, str):
161
- result.add(id(value))
162
- return result
163
-
164
-
165
153
  def _mask_dialect_noise(sql: str) -> str:
166
154
  sql = _QUOTED_IDENTIFIER.sub(lambda match: " " * len(match.group(0)), sql)
167
155
  return _MYSQL_COMMENT.sub(lambda match: " " * len(match.group(0)), sql)
@@ -124,13 +124,16 @@ class PreferFstringOverConcat(Rule):
124
124
  summary="Prefer f-strings for short human-readable interpolation when they make the result clearer.",
125
125
  rationale="For prose-like strings, f-strings keep interpolated values and surrounding text visible together.",
126
126
  remediation=(
127
- "Replace the interpolation with one f-string. Preserve an explicit `str(value)` conversion as `{value!s}` "
128
- "and `repr(value)` as `{value!r}` when those conversion semantics matter."
127
+ "Use one f-string when it preserves the required conversion and operator semantics. Preserve an explicit "
128
+ "`str(value)` conversion as `{value!s}` "
129
+ "and `repr(value)` as `{value!r}` when those conversion semantics matter. Retain required custom "
130
+ "concatenation or formatting behavior; a `str` annotation alone does not prove equivalence."
129
131
  ),
130
132
  category=RuleCategory.STYLE,
131
133
  autofix=AutofixPolicy.NONE,
132
134
  limitations=(
133
135
  "Runtime operands must have concrete string evidence.",
136
+ "String evidence does not prove that custom concatenation operators and `__format__` agree; this remains manual review.",
134
137
  (
135
138
  "Logging, common direct SQL and regular-expression forms, lazy strings, ORM expressions, generated files, long "
136
139
  "literals, and punctuation-oriented fragment composition are excluded."
@@ -55,7 +55,9 @@ class PreferInjectedDependencyOverMonkeypatch(Rule):
55
55
  "fake when rebuilding the fixture would add wiring without improving the boundary. Keep attribute-existence "
56
56
  "checks enabled and the tested implementation intact. Suppress SARJ445 only at that call, naming the "
57
57
  "owned instance and injection seam. For inherently global boundaries or lookup interception, explain why "
58
- "injection would invalidate the test. Do not switch to bare assignment merely to avoid this rule."
58
+ "injection would invalidate the test. Establish the binding the exercised consumer actually reads, "
59
+ "including already-bound import aliases, and show that the oracle rejects a wrong consumer. "
60
+ "Do not switch to bare assignment merely to avoid this rule."
59
61
  ),
60
62
  category=RuleCategory.TESTING,
61
63
  autofix=AutofixPolicy.NONE,
@@ -65,7 +67,7 @@ class PreferInjectedDependencyOverMonkeypatch(Rule):
65
67
  "Environment, mapping, import-path, and working-directory mutations are intentionally allowed.",
66
68
  "Untyped handles are recognized only on pytest tests or fixtures; directly parametrized values are excluded.",
67
69
  "A nested function that captures a monkeypatch handle from an outer scope is not inferred.",
68
- "Instance ownership and collaborator contracts are not inferred; justified scoped patches need a local suppression.",
70
+ "Instance ownership, collaborator contracts, and active consumer lookups are not inferred; justified scoped patches need a local suppression.",
69
71
  ),
70
72
  examples=(
71
73
  RuleExample(
@@ -35,7 +35,7 @@ class RequirePreciseFactorySignature(ProjectRule):
35
35
  documentation: ClassVar[RuleDocumentation | None] = RuleDocumentation(
36
36
  default_level=Severity.WARNING,
37
37
  summary="Keep fixed keyword factory calls behind a precise callable signature.",
38
- rationale="Callable[..., T] erases argument checking, so misspelled keywords and incompatible injected factories pass strict typing and can fail at runtime.",
38
+ rationale="Callable[..., T] and functools.partial[T] erase argument checking at injected boundaries, so misspelled keywords and incompatible factories pass strict typing and can fail at runtime.",
39
39
  remediation=(
40
40
  "If factory substitution is unnecessary, call the existing constructor directly. Otherwise reuse an "
41
41
  "existing precise callable contract; use type[T] when the dependency requires a class, accounting "
@@ -46,7 +46,7 @@ class RequirePreciseFactorySignature(ProjectRule):
46
46
  category=RuleCategory.ARCHITECTURE,
47
47
  autofix=AutofixPolicy.NONE,
48
48
  limitations=(
49
- "Only undecorated module-level functions with proven standard Callable[..., OwnedClass] parameters directly called with at least one named keyword are considered. Imported classes require scanned first-party declarations.",
49
+ "Only undecorated module-level functions with proven standard Callable[..., OwnedClass] or functools.partial[OwnedClass] parameters directly called with at least one named keyword are considered. Imported classes require scanned first-party declarations; direct partial construction and calls remain native type-checker ownership.",
50
50
  "Tests, test support, generated code, unresolved/string/generic annotations, positional-only calls, unpacking, forwarding, nested captures, rebinding and ambiguous imports are excluded. Import capture checks are conservative across the file, so an unrelated local pattern, exception or walrus binding can prevent a warning.",
51
51
  "A fixed call demonstrates signature erasure, not a current runtime defect. Genuine dynamic APIs can use a reasoned suppression. No autofix: the correct contract depends on whether classes or function factories are intended.",
52
52
  ),
@@ -65,6 +65,36 @@ class RequirePreciseFactorySignature(ProjectRule):
65
65
  expected_count=1,
66
66
  public=True,
67
67
  ),
68
+ RuleExample(
69
+ example_id="erased-partial-factory",
70
+ title="An injected partial retains its result type but loses argument checking",
71
+ outcome=ExampleOutcome.MATCH,
72
+ files=(
73
+ ExampleFile.python(
74
+ "app/service.py",
75
+ "from functools import partial\nclass Result: pass\ndef build(factory: partial[Result]):\n return factory(size=4)\n",
76
+ ),
77
+ ),
78
+ focus_path=PurePosixPath("app/service.py"),
79
+ expected_count=1,
80
+ scenario="partial-erasure",
81
+ public=True,
82
+ ),
83
+ RuleExample(
84
+ example_id="direct-partial-factory",
85
+ title="Direct partial construction keeps native argument checking",
86
+ outcome=ExampleOutcome.NO_MATCH,
87
+ files=(
88
+ ExampleFile.python(
89
+ "app/service.py",
90
+ "from functools import partial\nclass Result:\n def __init__(self, *, size: int):\n self.size = size\ndef build():\n factory = partial(Result)\n return factory(size=4)\n",
91
+ ),
92
+ ),
93
+ focus_path=PurePosixPath("app/service.py"),
94
+ expected_count=0,
95
+ scenario="partial-erasure",
96
+ public=True,
97
+ ),
68
98
  RuleExample(
69
99
  example_id="exact-factory",
70
100
  title="Factory declares its keyword contract",
@@ -113,7 +143,7 @@ class RequirePreciseFactorySignature(ProjectRule):
113
143
  col=argument.col_offset + 1,
114
144
  code=self.code,
115
145
  message=(
116
- f"`{argument.arg}` is called with fixed keywords but Callable[..., T] erases its "
146
+ f"`{argument.arg}` is called with fixed keywords but its factory annotation erases its "
117
147
  "argument contract; remove unnecessary factory indirection or reuse a precise callable "
118
148
  "contract that preserves the accepted keywords."
119
149
  ),
@@ -124,19 +154,20 @@ class RequirePreciseFactorySignature(ProjectRule):
124
154
 
125
155
 
126
156
  def _factory_result(annotation: ast.expr | None, context: PythonFileContext) -> ast.Name | ast.Attribute | None:
127
- if (
128
- not isinstance(annotation, ast.Subscript)
129
- or context.module_imports.resolved_qualified_name(annotation.value) not in _CALLABLES
130
- ):
157
+ if not isinstance(annotation, ast.Subscript):
131
158
  return None
132
- if not isinstance(annotation.slice, ast.Tuple) or len(annotation.slice.elts) != _CALLABLE_ARITY:
159
+ carrier = context.module_imports.resolved_qualified_name(annotation.value)
160
+ if carrier in _CALLABLES:
161
+ if not isinstance(annotation.slice, ast.Tuple) or len(annotation.slice.elts) != _CALLABLE_ARITY:
162
+ return None
163
+ parameters, result = annotation.slice.elts
164
+ if not isinstance(parameters, ast.Constant) or parameters.value is not Ellipsis:
165
+ return None
166
+ elif carrier == "functools.partial":
167
+ result = annotation.slice
168
+ else:
133
169
  return None
134
- parameters, result = annotation.slice.elts
135
- if (
136
- not isinstance(parameters, ast.Constant)
137
- or parameters.value is not Ellipsis
138
- or not isinstance(result, (ast.Name, ast.Attribute))
139
- ):
170
+ if not isinstance(result, (ast.Name, ast.Attribute)):
140
171
  return None
141
172
  root = _root(annotation.value)
142
173
  if (root is not None and _captured_import_root(root, context)) or any(