sarj-python-lint 1.27.0__tar.gz → 1.27.2__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 (188) hide show
  1. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/PKG-INFO +2 -2
  2. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/pyproject.toml +2 -2
  3. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/__init__.py +1 -0
  4. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/__main__.py +1 -1
  5. sarj_python_lint-1.27.2/src/sarj_python_lint/_source.py +18 -0
  6. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/ratchet.py +3 -3
  7. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_fastapi.py +1 -1
  8. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_local_source.py +2 -2
  9. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_nominal_project.py +3 -3
  10. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_project_index.py +1 -1
  11. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +1 -1
  12. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +2 -2
  13. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/require_injectable_retry_sleep.py +49 -14
  14. sarj_python_lint-1.27.0/src/sarj_python_lint/_source.py +0 -14
  15. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/.gitignore +0 -0
  16. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/LICENSE +0 -0
  17. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/README.md +0 -0
  18. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/_analysis_session.py +0 -0
  19. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/_file_context.py +0 -0
  20. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/_filesystem.py +0 -0
  21. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/_python_target.py +0 -0
  22. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/_ratchet_cli.py +0 -0
  23. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/_secret_names.py +0 -0
  24. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/_version.py +0 -0
  25. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/benchmark.py +0 -0
  26. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/interpreter_argv.py +0 -0
  27. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/json_boundary.py +0 -0
  28. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/py.typed +0 -0
  29. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rule_base.py +0 -0
  30. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/__init__.py +0 -0
  31. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
  32. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_ast_index.py +0 -0
  33. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_ast_position.py +0 -0
  34. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_comments.py +0 -0
  35. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
  36. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_copy_on_write.py +0 -0
  37. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
  38. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_docstrings.py +0 -0
  39. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_first_party.py +0 -0
  40. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
  41. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_imports.py +0 -0
  42. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_logging.py +0 -0
  43. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_paths.py +0 -0
  44. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
  45. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_pytest.py +0 -0
  46. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_registry.py +0 -0
  47. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_resource_provenance.py +0 -0
  48. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_set_fields.py +0 -0
  49. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_sql.py +0 -0
  50. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
  51. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
  52. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_test_composition.py +0 -0
  53. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/_test_provenance.py +0 -0
  54. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
  55. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
  56. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
  57. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
  58. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
  59. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
  60. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
  61. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
  62. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
  63. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
  64. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
  65. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
  66. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
  67. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
  68. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
  69. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
  70. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_before_validator_input_mutation.py +0 -0
  71. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_broad_keyword_capture.py +0 -0
  72. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
  73. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
  74. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
  75. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
  76. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
  77. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
  78. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
  79. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
  80. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
  81. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
  82. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
  83. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
  84. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
  85. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_fixed_sleep_in_loop.py +0 -0
  86. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
  87. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
  88. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
  89. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_input_model_mutation.py +0 -0
  90. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_interpreter_source_arguments.py +0 -0
  91. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
  92. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_mocked_pydantic_value_object.py +0 -0
  93. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
  94. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
  95. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
  96. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
  97. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
  98. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
  99. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
  100. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
  101. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
  102. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
  103. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
  104. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
  105. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
  106. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
  107. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_select_star.py +0 -0
  108. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
  109. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_shallow_container_type_guard.py +0 -0
  110. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
  111. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
  112. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
  113. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_test_method_grafting.py +0 -0
  114. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
  115. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
  116. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
  117. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_unused_copy_result.py +0 -0
  118. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_unused_underscored_keyword_parameter.py +0 -0
  119. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
  120. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
  121. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
  122. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
  123. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
  124. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
  125. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_autospec_for_callable_mock.py +0 -0
  126. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
  127. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
  128. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
  129. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
  130. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
  131. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
  132. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
  133. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
  134. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
  135. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
  136. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
  137. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
  138. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
  139. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
  140. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
  141. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_native_string_check.py +0 -0
  142. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
  143. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
  144. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
  145. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
  146. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_pytest_fixture_injection.py +0 -0
  147. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
  148. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_required_constructor_parameters.py +0 -0
  149. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
  150. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
  151. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
  152. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
  153. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
  154. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
  155. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
  156. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
  157. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
  158. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
  159. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
  160. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
  161. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
  162. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
  163. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
  164. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
  165. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
  166. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
  167. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
  168. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
  169. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
  170. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
  171. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
  172. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/require_precise_factory_signature.py +0 -0
  173. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
  174. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
  175. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
  176. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
  177. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
  178. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
  179. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/stepdown.py +0 -0
  180. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
  181. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
  182. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
  183. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
  184. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
  185. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
  186. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
  187. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
  188. {sarj_python_lint-1.27.0 → sarj_python_lint-1.27.2}/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.27.0
3
+ Version: 1.27.2
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/
@@ -19,7 +19,7 @@ Classifier: Topic :: Software Development :: Quality Assurance
19
19
  Requires-Python: >=3.14
20
20
  Requires-Dist: packaging==26.3
21
21
  Requires-Dist: ruff>=0.16.10
22
- Requires-Dist: sarj-rule-contracts==0.2.1
22
+ Requires-Dist: sarj-rule-contracts==0.2.2
23
23
  Requires-Dist: sqlglot==30.19.0
24
24
  Requires-Dist: typer==0.27.2
25
25
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sarj-python-lint"
3
- version = "1.27.0"
3
+ version = "1.27.2"
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" }]
@@ -15,7 +15,7 @@ classifiers = [
15
15
  "Programming Language :: Python :: 3.14",
16
16
  "Topic :: Software Development :: Quality Assurance",
17
17
  ]
18
- dependencies = ["sarj-rule-contracts==0.2.1", "packaging==26.3", "ruff>=0.16.10", "sqlglot==30.19.0", "typer==0.27.2"]
18
+ dependencies = ["sarj-rule-contracts==0.2.2", "packaging==26.3", "ruff>=0.16.10", "sqlglot==30.19.0", "typer==0.27.2"]
19
19
 
20
20
  [project.scripts]
21
21
  sarj-python-lint = "sarj_python_lint.__main__:main"
@@ -1,2 +1,3 @@
1
1
  from sarj_python_lint._ratchet_cli import run_ratchet as run_ratchet
2
+ from sarj_python_lint._source import python_source_encoding as python_source_encoding
2
3
  from sarj_python_lint._version import __version__ as __version__
@@ -434,7 +434,7 @@ def _apply_baseline(
434
434
  def _run_check(rule: list[str], files: list[Path], baseline: Path | None, update_baseline: Path | None) -> int:
435
435
  try:
436
436
  diags = analyze(rule, files)
437
- except (OSError, ValueError) as exc:
437
+ except (OSError, ValueError, SyntaxError) as exc:
438
438
  sys.stderr.write(f"error: {exc}\n")
439
439
  return 2
440
440
  if update_baseline is not None:
@@ -0,0 +1,18 @@
1
+ from __future__ import annotations
2
+
3
+ from io import BytesIO
4
+ import tokenize
5
+ from typing import TYPE_CHECKING
6
+
7
+
8
+ if TYPE_CHECKING:
9
+ from pathlib import Path
10
+
11
+
12
+ def python_source_encoding(data: bytes) -> str:
13
+ return tokenize.detect_encoding(BytesIO(data).readline)[0]
14
+
15
+
16
+ def read_python_source(path: Path) -> str:
17
+ with tokenize.open(path) as stream:
18
+ return stream.read()
@@ -9,7 +9,7 @@ import tokenize
9
9
  from types import MappingProxyType
10
10
  from typing import TYPE_CHECKING, Final, NamedTuple, TypeGuard
11
11
 
12
- from sarj_python_lint._source import PYTHON_SOURCE_ENCODING
12
+ from sarj_python_lint._source import read_python_source
13
13
  from sarj_python_lint.json_boundary import is_object_mapping, parse_json
14
14
 
15
15
 
@@ -330,8 +330,8 @@ def _python_files(root: Path, excluded_dir_names: frozenset[str]) -> Iterator[Pa
330
330
 
331
331
  def _read(path: Path) -> str:
332
332
  try:
333
- return path.read_text(encoding=PYTHON_SOURCE_ENCODING)
334
- except UnicodeDecodeError, OSError:
333
+ return read_python_source(path)
334
+ except UnicodeError, OSError, SyntaxError:
335
335
  return ""
336
336
 
337
337
 
@@ -841,7 +841,7 @@ def _read_module(path: Path, cache: dict[Path, ast.Module | None]) -> ast.Module
841
841
  tree = None
842
842
  else:
843
843
  tree = ast.parse(read_python_source(path), filename=str(path))
844
- except OSError, SyntaxError:
844
+ except OSError, SyntaxError, UnicodeError:
845
845
  tree = None
846
846
  cache[path] = tree
847
847
  return tree
@@ -5,7 +5,7 @@ from dataclasses import dataclass
5
5
  from pathlib import Path
6
6
  from typing import TYPE_CHECKING, final
7
7
 
8
- from sarj_python_lint._source import PYTHON_SOURCE_ENCODING
8
+ from sarj_python_lint._source import python_source_encoding
9
9
  from sarj_python_lint.rules._imports import ImportIndex
10
10
 
11
11
 
@@ -114,7 +114,7 @@ def _read_module(path: Path) -> LocalModule | None:
114
114
  data = stream.read(_MAX_MODULE_BYTES + 1)
115
115
  if len(data) > _MAX_MODULE_BYTES:
116
116
  return None
117
- source = data.decode(PYTHON_SOURCE_ENCODING)
117
+ source = data.decode(python_source_encoding(data))
118
118
  tree = ast.parse(source, filename=str(path))
119
119
  except OSError, UnicodeError, SyntaxError:
120
120
  return None
@@ -11,7 +11,7 @@ from typing import TYPE_CHECKING, TypeGuard, final
11
11
  from packaging.requirements import InvalidRequirement, Requirement
12
12
  from packaging.utils import canonicalize_name
13
13
 
14
- from sarj_python_lint._source import PYTHON_SOURCE_ENCODING
14
+ from sarj_python_lint._source import read_python_source
15
15
  from sarj_python_lint.rules._first_party import distribution_root
16
16
  from sarj_python_lint.rules._imports import ImportIndex
17
17
  from sarj_python_lint.rules._paths import is_generated
@@ -265,10 +265,10 @@ def _source(path: Path) -> NominalSource | None:
265
265
  try:
266
266
  if path.is_symlink() or path.stat().st_size > _MAX_BYTES:
267
267
  return None
268
- text = path.read_text(encoding=PYTHON_SOURCE_ENCODING)
268
+ text = read_python_source(path)
269
269
  if is_generated(path, text):
270
270
  return None
271
- except OSError, UnicodeError:
271
+ except OSError, UnicodeError, SyntaxError:
272
272
  return None
273
273
  return NominalSource(path, text)
274
274
 
@@ -534,7 +534,7 @@ def _read_bounded_source(root: Path, path: Path) -> LoadedSource | None:
534
534
  resolved = path.resolve()
535
535
  resolved.relative_to(root.resolve())
536
536
  return LoadedSource(resolved, read_python_source(resolved))
537
- except OSError, ValueError:
537
+ except OSError, ValueError, SyntaxError:
538
538
  return None
539
539
 
540
540
 
@@ -690,7 +690,7 @@ def _read_module(path: Path) -> ast.Module | None:
690
690
  if path.stat().st_size > _MAX_IMPORTED_MODULE_BYTES:
691
691
  return None
692
692
  tree = ast.parse(read_python_source(path), filename=str(path))
693
- except OSError, SyntaxError:
693
+ except OSError, SyntaxError, UnicodeError:
694
694
  return None
695
695
  return tree
696
696
 
@@ -720,7 +720,7 @@ def _read_module(path: Path, facts: RuntimeConfigFacts) -> ast.Module:
720
720
  return cached
721
721
  try:
722
722
  tree = ast.parse(read_python_source(path), filename=str(path))
723
- except OSError, SyntaxError:
723
+ except OSError, SyntaxError, UnicodeError:
724
724
  tree = ast.Module(body=[], type_ignores=[])
725
725
  facts.modules[path] = tree
726
726
  return tree
@@ -783,7 +783,7 @@ def _candidate_calls_class(
783
783
  return False
784
784
  try:
785
785
  source = read_python_source(candidate)
786
- except OSError:
786
+ except OSError, SyntaxError, UnicodeError:
787
787
  return False
788
788
  if class_name not in source or is_generated(candidate, source):
789
789
  return False
@@ -29,7 +29,7 @@ _POLICIES: Final = frozenset(
29
29
  )
30
30
  _WAIT_MODULES: Final = frozenset({"tenacity", "tenacity.wait"})
31
31
  _WALL_CLOCK_SLEEPS: Final = frozenset(
32
- {"asyncio.sleep", "anyio.sleep", "trio.sleep", "time.sleep", "tenacity.nap.sleep"}
32
+ {"asyncio.sleep", "anyio.sleep", "trio.sleep", "time.sleep", "tenacity.sleep", "tenacity.nap.sleep"}
33
33
  )
34
34
  _MESSAGE: Final = (
35
35
  "this tenacity policy waits between attempts on the wall clock without a sleep= seam; pass an injected sleep "
@@ -58,8 +58,8 @@ class RequireInjectableRetrySleep(Rule):
58
58
  autofix=AutofixPolicy.NONE,
59
59
  limitations=(
60
60
  "Only calls to tenacity.retry, tenacity.Retrying, and tenacity.AsyncRetrying resolved through imports are inspected; bare @retry, positional arguments, and **kwargs expansion abstain.",
61
- "A wait is reported only when it is built inline from tenacity wait_* calls, optionally combined with +; wait_none() and wait_fixed(0) do not wait. A wait read from a name, attribute, or other call is treated as injected.",
62
- "Any sleep= argument other than a wall-clock sleep imported from asyncio, anyio, trio, time, or tenacity.nap counts as a seam; the rule does not prove that tests replace it.",
61
+ "A wait is reported only when it is built inline from tenacity wait_* calls, optionally combined with +; wait_none(), literal-zero wait_fixed and wait_random calls, and known zero-only wait_combine or wait_chain calls do not wait. A wait read from a name, attribute, or other call is treated as injected.",
62
+ "Any sleep= argument other than a wall-clock sleep imported from asyncio, anyio, trio, time, tenacity, or tenacity.nap counts as a seam; the rule does not prove that tests replace it.",
63
63
  "The backoff library has no sleep parameter, so its decorators are not inspected. Test modules, test directories, conftest.py, test-support paths, and generated files are excluded.",
64
64
  ),
65
65
  examples=(
@@ -143,20 +143,55 @@ def _waits_without_seam(call: ast.Call, imports: ImportIndex) -> bool:
143
143
  def _wait_strategy(node: ast.expr, imports: ImportIndex) -> bool | None:
144
144
  match node:
145
145
  case ast.BinOp(left=left, op=ast.Add(), right=right):
146
- parts = (_wait_strategy(left, imports), _wait_strategy(right, imports))
147
- return None if None in parts else any(parts)
146
+ return _combined_waits((left, right), imports)
148
147
  case ast.Call(func=func, args=args, keywords=keywords):
149
148
  module, _, strategy = (imports.resolved_qualified_name(func) or "").rpartition(".")
150
149
  if module not in _WAIT_MODULES or not strategy.startswith("wait_"):
151
150
  return None
152
- match strategy, args, keywords:
153
- case "wait_none", [], []:
154
- return False
155
- case "wait_fixed", [ast.Constant(value=0)], []:
156
- return False
157
- case "wait_fixed", [], [ast.keyword(arg="wait", value=ast.Constant(value=0))]:
158
- return False
159
- case _:
160
- return True
151
+ return _wait_constructor(strategy, args, keywords, imports)
161
152
  case _:
162
153
  return None
154
+
155
+
156
+ def _wait_constructor(
157
+ strategy: str, args: list[ast.expr], keywords: list[ast.keyword], imports: ImportIndex
158
+ ) -> bool | None:
159
+ match strategy, args, keywords:
160
+ case "wait_none", [], []:
161
+ return False
162
+ case ("wait_fixed", [value], []) | ("wait_fixed", [], [ast.keyword(arg="wait", value=value)]):
163
+ return not _literal_zero(value)
164
+ case "wait_random", _, _:
165
+ return _random_waits(args, keywords)
166
+ case "wait_combine" | "wait_chain", _, _:
167
+ if keywords or any(isinstance(argument, ast.Starred) for argument in args):
168
+ return None
169
+ if strategy == "wait_chain" and not args:
170
+ return None
171
+ return _combined_waits(tuple(args), imports)
172
+ case _:
173
+ return True
174
+
175
+
176
+ def _combined_waits(nodes: tuple[ast.expr, ...], imports: ImportIndex) -> bool | None:
177
+ parts = tuple(_wait_strategy(node, imports) for node in nodes)
178
+ return None if None in parts else any(parts)
179
+
180
+
181
+ def _random_waits(args: list[ast.expr], keywords: list[ast.keyword]) -> bool | None:
182
+ names = ("min", "max")
183
+ if len(args) > len(names) or any(isinstance(argument, ast.Starred) for argument in args):
184
+ return None
185
+ values = dict(zip(names, args, strict=False))
186
+ for keyword in keywords:
187
+ if keyword.arg not in names or keyword.arg in values:
188
+ return None
189
+ values[keyword.arg] = keyword.value
190
+ return not (("min" not in values or _literal_zero(values["min"])) and _literal_zero(values.get("max")))
191
+
192
+
193
+ def _literal_zero(node: ast.expr | None) -> bool:
194
+ if isinstance(node, ast.UnaryOp) and isinstance(node.op, (ast.UAdd, ast.USub)):
195
+ operand = node.operand
196
+ return isinstance(operand, ast.Constant) and isinstance(operand.value, (int, float)) and operand.value == 0
197
+ return isinstance(node, ast.Constant) and node.value == 0
@@ -1,14 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import TYPE_CHECKING, Final
4
-
5
-
6
- if TYPE_CHECKING:
7
- from pathlib import Path
8
-
9
-
10
- PYTHON_SOURCE_ENCODING: Final = "utf-8-sig"
11
-
12
-
13
- def read_python_source(path: Path) -> str:
14
- return path.read_text(encoding=PYTHON_SOURCE_ENCODING, errors="replace")