sarj-python-lint 1.36.0__tar.gz → 1.37.0__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 (204) hide show
  1. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/PKG-INFO +1 -1
  2. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/pyproject.toml +1 -1
  3. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rule_base.py +1 -0
  4. sarj_python_lint-1.37.0/src/sarj_python_lint/rules/__init__.py +3 -0
  5. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_project_index.py +20 -17
  6. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_registry.py +7 -0
  7. sarj_python_lint-1.37.0/src/sarj_python_lint/rules/test_without_explicit_oracle.py +244 -0
  8. sarj_python_lint-1.36.0/src/sarj_python_lint/rules/__init__.py +0 -3
  9. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/.gitignore +0 -0
  10. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/LICENSE +0 -0
  11. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/README.md +0 -0
  12. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/__init__.py +0 -0
  13. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/__main__.py +0 -0
  14. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/_analysis_session.py +0 -0
  15. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/_file_context.py +0 -0
  16. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/_filesystem.py +0 -0
  17. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/_python_target.py +0 -0
  18. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/_ratchet_cli.py +0 -0
  19. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/_secret_names.py +0 -0
  20. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/_source.py +0 -0
  21. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/_version.py +0 -0
  22. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/benchmark.py +0 -0
  23. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/interpreter_argv.py +0 -0
  24. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/json_boundary.py +0 -0
  25. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/py.typed +0 -0
  26. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/ratchet.py +0 -0
  27. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
  28. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_ast_index.py +0 -0
  29. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_ast_position.py +0 -0
  30. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_closed_helpers.py +0 -0
  31. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_comments.py +0 -0
  32. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
  33. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_copy_on_write.py +0 -0
  34. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
  35. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_docstrings.py +0 -0
  36. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_exports.py +0 -0
  37. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_fastapi.py +0 -0
  38. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_first_party.py +0 -0
  39. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
  40. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_imports.py +0 -0
  41. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_inert_model_annotation.py +0 -0
  42. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_local_source.py +0 -0
  43. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_logging.py +0 -0
  44. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_namespace_access.py +0 -0
  45. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
  46. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_paths.py +0 -0
  47. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
  48. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_pydantic_field_contract.py +0 -0
  49. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_pytest.py +0 -0
  50. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_resolved_operation_signature.py +0 -0
  51. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_resource_provenance.py +0 -0
  52. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_set_fields.py +0 -0
  53. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_sql.py +0 -0
  54. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_static_json_alias.py +0 -0
  55. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
  56. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
  57. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_test_composition.py +0 -0
  58. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/_test_provenance.py +0 -0
  59. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
  60. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
  61. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
  62. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
  63. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
  64. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
  65. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/duplicate_adjacent_exception_handler.py +0 -0
  66. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/duplicate_closed_pure_helper.py +0 -0
  67. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
  68. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
  69. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
  70. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
  71. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
  72. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
  73. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
  74. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
  75. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
  76. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
  77. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
  78. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_before_validator_input_mutation.py +0 -0
  79. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_broad_keyword_capture.py +0 -0
  80. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
  81. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_conflicting_pydantic_alias_validation.py +0 -0
  82. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
  83. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
  84. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
  85. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
  86. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
  87. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
  88. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
  89. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
  90. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
  91. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
  92. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
  93. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
  94. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_fixed_sleep_in_loop.py +0 -0
  95. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
  96. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
  97. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
  98. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_impossible_pydantic_field_bounds.py +0 -0
  99. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
  100. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_input_model_mutation.py +0 -0
  101. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_interpreter_source_arguments.py +0 -0
  102. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
  103. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_mocked_pydantic_value_object.py +0 -0
  104. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
  105. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
  106. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
  107. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
  108. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
  109. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
  110. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
  111. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
  112. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
  113. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
  114. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
  115. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
  116. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
  117. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
  118. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_select_star.py +0 -0
  119. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
  120. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_shallow_container_type_guard.py +0 -0
  121. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
  122. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
  123. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
  124. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_test_method_grafting.py +0 -0
  125. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
  126. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
  127. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_unknown_pydantic_validator_field.py +0 -0
  128. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
  129. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_unused_copy_result.py +0 -0
  130. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_unused_underscored_keyword_parameter.py +0 -0
  131. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
  132. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
  133. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
  134. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
  135. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
  136. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
  137. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_autospec_for_callable_mock.py +0 -0
  138. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
  139. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
  140. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
  141. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_declarative_batch_validation.py +0 -0
  142. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
  143. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
  144. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
  145. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
  146. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
  147. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
  148. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
  149. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
  150. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
  151. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
  152. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
  153. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
  154. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_native_string_check.py +0 -0
  155. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
  156. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
  157. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
  158. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
  159. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_pytest_fixture_injection.py +0 -0
  160. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
  161. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_required_constructor_parameters.py +0 -0
  162. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
  163. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
  164. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
  165. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
  166. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
  167. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
  168. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
  169. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
  170. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
  171. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
  172. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
  173. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
  174. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
  175. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
  176. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/redundant_test_constructor_forwarder.py +0 -0
  177. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
  178. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
  179. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
  180. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/repeated_unimplemented_test_contract.py +0 -0
  181. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
  182. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
  183. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
  184. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_injectable_retry_sleep.py +0 -0
  185. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
  186. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
  187. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
  188. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_precise_factory_signature.py +0 -0
  189. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
  190. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
  191. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
  192. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
  193. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
  194. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
  195. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/stepdown.py +0 -0
  196. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
  197. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
  198. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
  199. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
  200. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
  201. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
  202. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
  203. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
  204. {sarj_python_lint-1.36.0 → sarj_python_lint-1.37.0}/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.36.0
3
+ Version: 1.37.0
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.36.0"
3
+ version = "1.37.0"
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" }]
@@ -94,6 +94,7 @@ class Rule(ABC):
94
94
  id: str
95
95
  code: str
96
96
  description: str
97
+ enabled_by_default: ClassVar[bool] = True
97
98
  documentation: ClassVar[RuleDocumentation | None] = None
98
99
  _analysis_session: AnalysisSession | None = None
99
100
 
@@ -0,0 +1,3 @@
1
+ from __future__ import annotations
2
+
3
+ from sarj_python_lint.rules._registry import DEFAULT_REGISTRY as DEFAULT_REGISTRY, REGISTRY as REGISTRY
@@ -566,20 +566,22 @@ class ProjectIndexSet:
566
566
  if isinstance(owner, ast.ClassDef) and _class_has_typed_dependency(candidate, owner, target)
567
567
  )
568
568
 
569
+ @cached_property
570
+ def _mock_specs(self) -> Mapping[SymbolRef, frozenset[Path]]:
571
+ specs: dict[SymbolRef, set[Path]] = {}
572
+ for candidate in self._units.values():
573
+ if candidate.tree is None:
574
+ continue
575
+ for call in walk_ast(candidate.tree):
576
+ if isinstance(call, ast.Call):
577
+ for target in _mock_spec_targets(candidate, call):
578
+ specs.setdefault(target, set()).add(candidate.path)
579
+ return MappingProxyType({target: frozenset(paths) for target, paths in specs.items()})
580
+
569
581
  def test_mock_specs(self, unit: SourceUnit, name: str) -> frozenset[Path]:
570
582
  if unit.module is None:
571
583
  return frozenset()
572
- target = SymbolRef(unit.module, name)
573
- return frozenset(
574
- candidate.path
575
- for candidate in self._units.values()
576
- if candidate.tree is not None
577
- and any(
578
- _is_mock_spec_for(candidate, call, target)
579
- for call in walk_ast(candidate.tree)
580
- if isinstance(call, ast.Call)
581
- )
582
- )
584
+ return self._mock_specs.get(SymbolRef(unit.module, name), frozenset())
583
585
 
584
586
  def class_inherits_from(self, unit: SourceUnit, name: str, qualified_bases: frozenset[str]) -> bool:
585
587
  if unit.module is None:
@@ -1350,14 +1352,15 @@ def _class_has_typed_dependency(unit: SourceUnit, owner: ast.ClassDef, target: S
1350
1352
  return False
1351
1353
 
1352
1354
 
1353
- def _is_mock_spec_for(unit: SourceUnit, call: ast.Call, target: SymbolRef) -> bool:
1355
+ def _mock_spec_targets(unit: SourceUnit, call: ast.Call) -> Iterator[SymbolRef]:
1354
1356
  mock_name = _mock_function_name(unit, call.func)
1355
1357
  if mock_name is None:
1356
- return False
1357
- return any(
1358
- keyword.arg in {"spec", "spec_set"} and _direct_base_symbol(unit, keyword.value) == target
1359
- for keyword in call.keywords
1360
- ) or (mock_name == "create_autospec" and bool(call.args) and _direct_base_symbol(unit, call.args[0]) == target)
1358
+ return
1359
+ for keyword in call.keywords:
1360
+ if keyword.arg in {"spec", "spec_set"} and (target := _direct_base_symbol(unit, keyword.value)) is not None:
1361
+ yield target
1362
+ if mock_name == "create_autospec" and call.args and (target := _direct_base_symbol(unit, call.args[0])) is not None:
1363
+ yield target
1361
1364
 
1362
1365
 
1363
1366
  def _mock_function_name(unit: SourceUnit, function: ast.expr) -> str | None:
@@ -215,10 +215,12 @@ from sarj_python_lint.rules.no_impossible_pydantic_field_bounds import NoImpossi
215
215
  from sarj_python_lint.rules.no_unknown_pydantic_validator_field import NoUnknownPydanticValidatorField
216
216
  from sarj_python_lint.rules.redundant_test_constructor_forwarder import RedundantTestConstructorForwarder
217
217
  from sarj_python_lint.rules.repeated_unimplemented_test_contract import RepeatedUnimplementedTestContract
218
+ from sarj_python_lint.rules.test_without_explicit_oracle import NoExplicitTestOracle
218
219
 
219
220
 
220
221
  REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
221
222
  {
223
+ NoExplicitTestOracle.id: NoExplicitTestOracle,
222
224
  NoConflictingPydanticAliasValidation.id: NoConflictingPydanticAliasValidation,
223
225
  NoUnknownPydanticValidatorField.id: NoUnknownPydanticValidatorField,
224
226
  NoImpossiblePydanticFieldBounds.id: NoImpossiblePydanticFieldBounds,
@@ -367,3 +369,8 @@ REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
367
369
  RepeatedUnimplementedTestContract.id: RepeatedUnimplementedTestContract,
368
370
  }
369
371
  )
372
+
373
+
374
+ DEFAULT_REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
375
+ {name: rule for name, rule in REGISTRY.items() if rule.enabled_by_default}
376
+ )
@@ -0,0 +1,244 @@
1
+ from __future__ import annotations
2
+
3
+ import ast
4
+ from pathlib import PurePosixPath
5
+ from typing import TYPE_CHECKING, ClassVar, final, override
6
+
7
+ from sarj_python_lint.rule_base import (
8
+ AutofixPolicy,
9
+ Diagnostic,
10
+ ExampleFile,
11
+ ExampleOutcome,
12
+ Rule,
13
+ RuleCategory,
14
+ RuleDocumentation,
15
+ RuleExample,
16
+ Severity,
17
+ )
18
+ from sarj_python_lint.rules._paths import is_test_path
19
+ from sarj_python_lint.rules._pytest import uses_benchmark_fixture
20
+ from sarj_python_lint.rules._test_assertions import names_verification, reads_as_verification
21
+
22
+
23
+ if TYPE_CHECKING:
24
+ from collections.abc import Iterator, Sequence
25
+
26
+ from sarj_python_lint._file_context import PythonFileContext
27
+ from sarj_python_lint.rules._imports import ImportIndex
28
+
29
+
30
+ type _Function = ast.FunctionDef | ast.AsyncFunctionDef
31
+ _NON_UNIT_MARKS = frozenset({"benchmark", "lto", "lto_judge", "skip", "skipif", "xfail"})
32
+
33
+
34
+ @final
35
+ class NoExplicitTestOracle(Rule):
36
+ id = "test-without-explicit-oracle"
37
+ code = "SARJ495"
38
+ enabled_by_default = False
39
+ documentation: ClassVar[RuleDocumentation | None] = RuleDocumentation(
40
+ default_level=Severity.WARNING,
41
+ summary="A collected test has no visible assertion or exception oracle.",
42
+ rationale=(
43
+ "A test can execute a side effect without observing whether the intended effect occurred. "
44
+ "The absence of an explicit oracle is a review candidate, not proof that a smoke or no-throw contract is weak."
45
+ ),
46
+ remediation=(
47
+ "Identify the intended contract. Reuse adequate existing coverage, observe the returned value, state, "
48
+ "or emitted effect, or retain the test when successful execution is itself the contract. Do not add "
49
+ "a tautological assertion or delete a test solely to silence this advisory."
50
+ ),
51
+ category=RuleCategory.TESTING,
52
+ autofix=AutofixPolicy.NONE,
53
+ limitations=(
54
+ "This opt-in advisory is excluded from default Python checks; select it explicitly or use code-standards observe.",
55
+ "Only default-collected test_* functions and methods of undecorated or pytest-marked, constructor-free, base-free Test* classes are inspected.",
56
+ "Assertions, explicit raises, verification-named calls, import aliases, and local helpers containing or forwarding an oracle are accepted conservatively.",
57
+ "Unknown decorators, test opt-outs, benchmark/LLM/skipped tests, and callable fixture parameters are left unreported; external helper behavior is not inferred.",
58
+ "An intentional no-throw test may match. Findings describe syntax and require contract review; they never authorize automatic deletion or error-level promotion.",
59
+ ),
60
+ examples=(
61
+ RuleExample(
62
+ example_id="unobserved-effect",
63
+ title="Observe whether the requested side effect occurred",
64
+ outcome=ExampleOutcome.MATCH,
65
+ files=(ExampleFile.python("tests/test_delivery.py", "def test_delivery():\n deliver('hello')\n"),),
66
+ focus_path=PurePosixPath("tests/test_delivery.py"),
67
+ expected_count=1,
68
+ public=True,
69
+ ),
70
+ RuleExample(
71
+ example_id="observed-effect",
72
+ title="The assertion distinguishes a missing delivery",
73
+ outcome=ExampleOutcome.NO_MATCH,
74
+ files=(
75
+ ExampleFile.python(
76
+ "tests/test_delivery.py",
77
+ "def test_delivery(sender):\n deliver('hello', sender)\n assert sender.messages == ['hello']\n",
78
+ ),
79
+ ),
80
+ focus_path=PurePosixPath("tests/test_delivery.py"),
81
+ expected_count=0,
82
+ public=True,
83
+ ),
84
+ ),
85
+ )
86
+ description = documentation.summary
87
+
88
+ @override
89
+ def check_context(self, context: PythonFileContext) -> list[Diagnostic]:
90
+ if not is_test_path(context.path) or context.path.name == "conftest.py" or context.generated:
91
+ return []
92
+ tree = context.tree
93
+ if tree is None or _opts_out(tree.body) or not _scope_marks(tree.body, context.imports):
94
+ return []
95
+ helpers = _oracle_helpers(tree, context.imports)
96
+ return [
97
+ Diagnostic(
98
+ path=context.path,
99
+ line=function.lineno,
100
+ col=function.col_offset + 1,
101
+ code=self.code,
102
+ severity=Severity.WARNING,
103
+ message=(
104
+ f"`{function.name}` has no visible assertion or exception oracle. Review the contract: "
105
+ "observe the outcome, reuse existing coverage, or retain an intentional smoke/no-throw test."
106
+ ),
107
+ )
108
+ for function in _collected_tests(tree, context.imports)
109
+ if not uses_benchmark_fixture(function)
110
+ and not _has_oracle(function, helpers, context.imports)
111
+ and not _calls_fixture(function)
112
+ ]
113
+
114
+
115
+ def _scope_nodes(function: _Function) -> Iterator[ast.AST]:
116
+ pending: list[ast.AST] = list(reversed(function.body))
117
+ while pending:
118
+ node = pending.pop()
119
+ yield node
120
+ if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef, ast.Lambda)):
121
+ pending.extend(reversed(list(ast.iter_child_nodes(node))))
122
+
123
+
124
+ def _has_oracle(function: _Function, helpers: frozenset[str], imports: ImportIndex) -> bool:
125
+ for node in _scope_nodes(function):
126
+ if isinstance(node, (ast.Assert, ast.Raise)):
127
+ return True
128
+ if isinstance(node, ast.Call):
129
+ if names_verification(node.func, helpers):
130
+ return True
131
+ if isinstance(node.func, ast.Attribute) and node.func.attr in helpers:
132
+ return True
133
+ qualified = imports.resolved_qualified_name(node.func)
134
+ if qualified is not None and reads_as_verification(qualified.rsplit(".", 1)[-1]):
135
+ return True
136
+ return False
137
+
138
+
139
+ def _oracle_helpers(tree: ast.Module, imports: ImportIndex) -> frozenset[str]:
140
+ functions = [node for node in ast.walk(tree) if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))]
141
+ helpers: frozenset[str] = frozenset()
142
+ while True:
143
+ updated = helpers | frozenset(
144
+ function.name for function in functions if _has_oracle(function, helpers, imports)
145
+ )
146
+ if updated == helpers:
147
+ return helpers
148
+ helpers = updated
149
+
150
+
151
+ def _opts_out(statements: Sequence[ast.stmt]) -> bool:
152
+ return any(
153
+ isinstance(target, ast.Name)
154
+ and target.id == "__test__"
155
+ and (not isinstance(value, ast.Constant) or value.value is not True)
156
+ for target, value in _assignments(statements)
157
+ )
158
+
159
+
160
+ def _assignments(statements: Sequence[ast.stmt]) -> Iterator[tuple[ast.expr, ast.expr]]:
161
+ for statement in statements:
162
+ if isinstance(statement, ast.Assign):
163
+ for target in statement.targets:
164
+ yield target, statement.value
165
+ elif isinstance(statement, ast.AnnAssign) and statement.value is not None:
166
+ yield statement.target, statement.value
167
+
168
+
169
+ def _scope_marks(statements: Sequence[ast.stmt], imports: ImportIndex) -> bool:
170
+ for target, value in _assignments(statements):
171
+ if isinstance(target, ast.Name) and target.id == "pytestmark":
172
+ marks = value.elts if isinstance(value, (ast.List, ast.Tuple)) else [value]
173
+ if not _safe_marks(marks, imports):
174
+ return False
175
+ return True
176
+
177
+
178
+ def _disabled_tests(statements: Sequence[ast.stmt]) -> frozenset[str]:
179
+ return frozenset(
180
+ ast.unparse(target.value)
181
+ for target, value in _assignments(statements)
182
+ if (
183
+ isinstance(target, ast.Attribute)
184
+ and target.attr == "__test__"
185
+ and (not isinstance(value, ast.Constant) or value.value is not True)
186
+ )
187
+ )
188
+
189
+
190
+ def _safe_marks(decorators: Sequence[ast.expr], imports: ImportIndex) -> bool:
191
+ for decorator in decorators:
192
+ target = decorator.func if isinstance(decorator, ast.Call) else decorator
193
+ qualified = imports.resolved_qualified_name(target)
194
+ if qualified is None or not qualified.startswith("pytest.mark."):
195
+ return False
196
+ if qualified.rsplit(".", 1)[-1] in _NON_UNIT_MARKS:
197
+ return False
198
+ return True
199
+
200
+
201
+ def _collected_tests(tree: ast.Module, imports: ImportIndex) -> Iterator[_Function]:
202
+ disabled = _disabled_tests(tree.body)
203
+ for statement in tree.body:
204
+ if isinstance(statement, (ast.FunctionDef, ast.AsyncFunctionDef)):
205
+ if _test_function(statement, disabled, imports):
206
+ yield statement
207
+ elif isinstance(statement, ast.ClassDef) and statement.name not in disabled and _test_class(statement, imports):
208
+ yield from _class_tests(statement, disabled, imports)
209
+
210
+
211
+ def _class_tests(node: ast.ClassDef, disabled: frozenset[str], imports: ImportIndex) -> Iterator[_Function]:
212
+ class_disabled = _disabled_tests(node.body) | frozenset(
213
+ name.removeprefix(f"{node.name}.") for name in disabled if name.startswith(f"{node.name}.")
214
+ )
215
+ for child in node.body:
216
+ if isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef)) and _test_function(
217
+ child, class_disabled, imports
218
+ ):
219
+ yield child
220
+
221
+
222
+ def _test_function(node: _Function, disabled: frozenset[str], imports: ImportIndex) -> bool:
223
+ return node.name.startswith("test_") and node.name not in disabled and _safe_marks(node.decorator_list, imports)
224
+
225
+
226
+ def _test_class(node: ast.ClassDef, imports: ImportIndex) -> bool:
227
+ if not node.name.startswith("Test") or node.bases or node.keywords or _opts_out(node.body):
228
+ return False
229
+ if any(
230
+ isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef)) and child.name in {"__init__", "__new__"}
231
+ for child in node.body
232
+ ):
233
+ return False
234
+ return _safe_marks(node.decorator_list, imports) and _scope_marks(node.body, imports)
235
+
236
+
237
+ def _calls_fixture(function: _Function) -> bool:
238
+ parameters = {
239
+ argument.arg for argument in (*function.args.posonlyargs, *function.args.args, *function.args.kwonlyargs)
240
+ }
241
+ return any(
242
+ isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id in parameters
243
+ for node in _scope_nodes(function)
244
+ )
@@ -1,3 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from sarj_python_lint.rules._registry import REGISTRY as REGISTRY