sarj-python-lint 1.7.6__tar.gz → 1.7.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/PKG-INFO +1 -1
  2. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/pyproject.toml +1 -1
  3. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_registry.py +2 -0
  4. sarj_python_lint-1.7.8/src/sarj_python_lint/rules/discourage_nullable_constructor_parameters.py +122 -0
  5. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +4 -5
  6. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/.gitignore +0 -0
  7. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/LICENSE +0 -0
  8. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/README.md +0 -0
  9. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/__init__.py +0 -0
  10. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/__main__.py +0 -0
  11. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/_analysis_session.py +0 -0
  12. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/_file_context.py +0 -0
  13. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/_filesystem.py +0 -0
  14. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/_python_target.py +0 -0
  15. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/_ratchet_cli.py +0 -0
  16. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/_secret_names.py +0 -0
  17. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/_version.py +0 -0
  18. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/benchmark.py +0 -0
  19. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/json_boundary.py +0 -0
  20. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/py.typed +0 -0
  21. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/ratchet.py +0 -0
  22. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rule_base.py +0 -0
  23. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/__init__.py +0 -0
  24. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
  25. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_ast_index.py +0 -0
  26. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_ast_position.py +0 -0
  27. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_comments.py +0 -0
  28. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
  29. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
  30. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_docstrings.py +0 -0
  31. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_fastapi.py +0 -0
  32. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_first_party.py +0 -0
  33. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
  34. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_imports.py +0 -0
  35. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_logging.py +0 -0
  36. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
  37. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_paths.py +0 -0
  38. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_project_index.py +0 -0
  39. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
  40. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_pytest.py +0 -0
  41. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_resource_provenance.py +0 -0
  42. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_set_fields.py +0 -0
  43. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_sql.py +0 -0
  44. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
  45. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
  46. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/_test_composition.py +0 -0
  47. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
  48. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
  49. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
  50. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
  51. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
  52. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
  53. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
  54. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
  55. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
  56. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
  57. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
  58. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
  59. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
  60. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
  61. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
  62. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
  63. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
  64. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
  65. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
  66. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
  67. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
  68. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
  69. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
  70. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
  71. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
  72. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
  73. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
  74. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
  75. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
  76. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
  77. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
  78. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
  79. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
  80. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
  81. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
  82. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
  83. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
  84. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
  85. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
  86. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
  87. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
  88. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
  89. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
  90. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
  91. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
  92. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_select_star.py +0 -0
  93. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
  94. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
  95. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
  96. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
  97. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
  98. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
  99. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
  100. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
  101. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
  102. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
  103. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
  104. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
  105. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
  106. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
  107. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
  108. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
  109. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
  110. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
  111. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
  112. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
  113. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
  114. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
  115. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
  116. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
  117. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
  118. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
  119. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
  120. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
  121. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
  122. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
  123. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
  124. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
  125. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
  126. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
  127. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
  128. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
  129. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
  130. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
  131. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
  132. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
  133. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
  134. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
  135. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
  136. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
  137. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
  138. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
  139. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
  140. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
  141. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
  142. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
  143. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
  144. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
  145. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
  146. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
  147. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
  148. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
  149. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
  150. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
  151. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
  152. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
  153. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
  154. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
  155. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/stepdown.py +0 -0
  156. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
  157. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
  158. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
  159. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
  160. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
  161. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
  162. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
  163. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
  164. {sarj_python_lint-1.7.6 → sarj_python_lint-1.7.8}/src/sarj_python_lint/rules/unused_test_factory_option.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sarj-python-lint
3
- Version: 1.7.6
3
+ Version: 1.7.8
4
4
  Summary: Custom Python lint rules — AST-based, pre-commit-friendly, hypermodern defaults
5
5
  Project-URL: Homepage, https://code-standards.sarj.ai/rules/python/
6
6
  Project-URL: Documentation, https://code-standards.sarj.ai/rules/python/
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sarj-python-lint"
3
- version = "1.7.6"
3
+ version = "1.7.8"
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" }]
@@ -9,6 +9,7 @@ from sarj_python_lint.rules.complex_postgres_query_requires_architecture_review
9
9
  ComplexPostgresQueryRequiresArchitectureReview,
10
10
  )
11
11
  from sarj_python_lint.rules.defect_xfail_requires_explicit_strict import DefectXfailRequiresExplicitStrict
12
+ from sarj_python_lint.rules.discourage_nullable_constructor_parameters import DiscourageNullableConstructorParameters
12
13
  from sarj_python_lint.rules.docstring_args_restate_signature import (
13
14
  DocstringArgsRestateSignature,
14
15
  )
@@ -192,6 +193,7 @@ if TYPE_CHECKING:
192
193
  REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
193
194
  {
194
195
  RequireExplicitContractImplementation.id: RequireExplicitContractImplementation,
196
+ DiscourageNullableConstructorParameters.id: DiscourageNullableConstructorParameters,
195
197
  RequirePublicDependencyContract.id: RequirePublicDependencyContract,
196
198
  RequireExplicitServiceContract.id: RequireExplicitServiceContract,
197
199
  AsyncCleanupRegisteredSynchronously.id: AsyncCleanupRegisteredSynchronously,
@@ -0,0 +1,122 @@
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
+
20
+
21
+ if TYPE_CHECKING:
22
+ from sarj_python_lint._file_context import PythonFileContext
23
+
24
+
25
+ @final
26
+ class DiscourageNullableConstructorParameters(Rule):
27
+ id = "discourage-nullable-constructor-parameters"
28
+ code = "SARJ468"
29
+ documentation: ClassVar[RuleDocumentation | None] = RuleDocumentation(
30
+ default_level=Severity.WARNING,
31
+ summary="Avoid nullable constructor parameters when a non-nullable interface can express the choice.",
32
+ rationale=(
33
+ "Nullable constructor parameters allow partially configured objects and force callers and implementations "
34
+ "to handle an absent value. Required nullable parameters still permit this state."
35
+ ),
36
+ remediation="Prefer a non-nullable dependency or a separate constructor for the absent case; suppress deliberate nullable values with a reason.",
37
+ category=RuleCategory.ARCHITECTURE,
38
+ autofix=AutofixPolicy.NONE,
39
+ limitations=(
40
+ "Only explicitly annotated `__init__` parameters using PEP 604 unions containing None are checked.",
41
+ "Tests and generated files are excluded; genuinely absent domain values may justify a suppression.",
42
+ ),
43
+ examples=(
44
+ RuleExample(
45
+ example_id="nullable-dependency",
46
+ title="Constructor permits a missing dependency",
47
+ outcome=ExampleOutcome.MATCH,
48
+ files=(
49
+ ExampleFile.python(
50
+ "app/service.py",
51
+ "class Service:\n def __init__(self, store: Store | None = None) -> None:\n"
52
+ " self.store = store\n",
53
+ ),
54
+ ),
55
+ focus_path=PurePosixPath("app/service.py"),
56
+ expected_count=1,
57
+ public=True,
58
+ ),
59
+ RuleExample(
60
+ example_id="non-nullable-dependency",
61
+ title="Constructor requires a usable dependency",
62
+ outcome=ExampleOutcome.NO_MATCH,
63
+ files=(
64
+ ExampleFile.python(
65
+ "app/service.py",
66
+ "class Service:\n def __init__(self, store: Store) -> None:\n self.store = store\n",
67
+ ),
68
+ ),
69
+ focus_path=PurePosixPath("app/service.py"),
70
+ expected_count=0,
71
+ public=True,
72
+ ),
73
+ ),
74
+ )
75
+ description = documentation.summary
76
+
77
+ @override
78
+ def check_context(self, context: PythonFileContext) -> list[Diagnostic]:
79
+ if context.generated or context.tree is None or is_test_path(context.path):
80
+ return []
81
+ findings: list[Diagnostic] = []
82
+ for node in ast.walk(context.tree):
83
+ if not isinstance(node, ast.ClassDef):
84
+ continue
85
+ for method in node.body:
86
+ if not isinstance(method, ast.FunctionDef | ast.AsyncFunctionDef) or method.name != "__init__":
87
+ continue
88
+ positional = method.args.posonlyargs + method.args.args
89
+ for parameter in (*positional, *method.args.kwonlyargs):
90
+ self._append_if_nullable(context, parameter, findings)
91
+ return findings
92
+
93
+ def _append_if_nullable(
94
+ self,
95
+ context: PythonFileContext,
96
+ parameter: ast.arg,
97
+ findings: list[Diagnostic],
98
+ ) -> None:
99
+ if parameter.annotation is None or not _is_nullable_union(parameter.annotation):
100
+ return
101
+ findings.append(
102
+ Diagnostic(
103
+ path=context.path,
104
+ line=parameter.lineno,
105
+ col=parameter.col_offset + 1,
106
+ code=self.code,
107
+ severity=Severity.WARNING,
108
+ message=f"Constructor parameter `{parameter.arg}` accepts None; prefer a non-nullable interface.",
109
+ )
110
+ )
111
+
112
+
113
+ def _is_nullable_union(annotation: ast.expr) -> bool:
114
+ if not isinstance(annotation, ast.BinOp) or not isinstance(annotation.op, ast.BitOr):
115
+ return False
116
+ return _is_none_union_member(annotation.left) or _is_none_union_member(annotation.right)
117
+
118
+
119
+ def _is_none_union_member(annotation: ast.expr) -> bool:
120
+ if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
121
+ return _is_none_union_member(annotation.left) or _is_none_union_member(annotation.right)
122
+ return isinstance(annotation, ast.Constant) and annotation.value is None
@@ -20,7 +20,6 @@ from sarj_python_lint.rule_base import (
20
20
  )
21
21
  from sarj_python_lint.rules._ast_index import walk as walk_ast
22
22
  from sarj_python_lint.rules._imports import ImportIndex
23
- from sarj_python_lint.rules._paths import is_test_path, is_test_support_path
24
23
 
25
24
 
26
25
  if TYPE_CHECKING:
@@ -38,7 +37,7 @@ _EXECUTE_METHODS = frozenset({"execute", "executemany"})
38
37
  _REFLECTIVE_WRITE_ARG_COUNT = 2
39
38
  _LANGUAGE_ROOT_PATH_DEPTH = 2
40
39
  _PACKAGE_ROOT_PATH_DEPTH = 3
41
- _OPERATIONAL_ROOTS = frozenset({"backfill", "backfills", "bin", "scripts", "test_support", "tools"})
40
+ _OPERATIONAL_ROOTS = frozenset({"backfill", "backfills", "bin", "scripts", "tools"})
42
41
 
43
42
 
44
43
  class _Origin(StrEnum):
@@ -87,7 +86,7 @@ class NoPsycopgExecutionOutsideInjectedOwner(Rule):
87
86
  limitations=(
88
87
  "Only import-proven psycopg Connection or AsyncConnection and psycopg_pool ConnectionPool or AsyncConnectionPool flows are classified.",
89
88
  "Straightforward constructor injection, connection and cursor context managers, aliases, rebindings, and conservative control-flow joins are followed; interprocedural flows remain unreported.",
90
- "Tests, test support, migrations, generated files, conventional operational-script roots, and literal SELECT 1 probes are excluded.",
89
+ "Collected test modules, conftest.py, migrations, generated files, conventional operational-script roots, and literal SELECT 1 probes are excluded. Shared test-support helpers must use an injected owner.",
91
90
  ),
92
91
  examples=(
93
92
  RuleExample(
@@ -126,10 +125,10 @@ class NoPsycopgExecutionOutsideInjectedOwner(Rule):
126
125
  def check_context(self, context: PythonFileContext) -> list[Diagnostic]:
127
126
  path = context.path
128
127
  path_parts = {part.lower() for part in path.parts}
128
+ collected_test = path.name == "conftest.py" or path.name.startswith("test_") or path.name.endswith("_test.py")
129
129
  if (
130
130
  context.generated
131
- or is_test_path(path)
132
- or is_test_support_path(path)
131
+ or collected_test
133
132
  or not path_parts.isdisjoint({"migration", "migrations"})
134
133
  or _is_operational_path(path)
135
134
  ):