sarj-python-lint 1.9.0__tar.gz → 1.9.1__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 (166) hide show
  1. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/PKG-INFO +1 -1
  2. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/pyproject.toml +1 -1
  3. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_registry.py +2 -0
  4. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_sql.py +16 -2
  5. sarj_python_lint-1.9.1/src/sarj_python_lint/rules/no_database_programmability.py +118 -0
  6. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/.gitignore +0 -0
  7. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/LICENSE +0 -0
  8. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/README.md +0 -0
  9. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/__init__.py +0 -0
  10. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/__main__.py +0 -0
  11. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/_analysis_session.py +0 -0
  12. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/_file_context.py +0 -0
  13. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/_filesystem.py +0 -0
  14. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/_python_target.py +0 -0
  15. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/_ratchet_cli.py +0 -0
  16. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/_secret_names.py +0 -0
  17. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/_version.py +0 -0
  18. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/benchmark.py +0 -0
  19. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/json_boundary.py +0 -0
  20. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/py.typed +0 -0
  21. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/ratchet.py +0 -0
  22. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rule_base.py +0 -0
  23. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/__init__.py +0 -0
  24. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
  25. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_ast_index.py +0 -0
  26. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_ast_position.py +0 -0
  27. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_comments.py +0 -0
  28. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
  29. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
  30. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_docstrings.py +0 -0
  31. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_fastapi.py +0 -0
  32. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_first_party.py +0 -0
  33. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
  34. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_imports.py +0 -0
  35. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_logging.py +0 -0
  36. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
  37. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_paths.py +0 -0
  38. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_project_index.py +0 -0
  39. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
  40. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_pytest.py +0 -0
  41. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_resource_provenance.py +0 -0
  42. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_set_fields.py +0 -0
  43. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
  44. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
  45. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/_test_composition.py +0 -0
  46. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
  47. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
  48. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
  49. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
  50. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/discourage_nullable_constructor_parameters.py +0 -0
  51. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
  52. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
  53. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
  54. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
  55. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
  56. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
  57. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
  58. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
  59. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
  60. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
  61. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
  62. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
  63. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
  64. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
  65. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
  66. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
  67. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
  68. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
  69. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
  70. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
  71. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
  72. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
  73. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
  74. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
  75. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
  76. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
  77. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
  78. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
  79. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
  80. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
  81. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
  82. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
  83. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
  84. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
  85. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
  86. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
  87. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
  88. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
  89. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
  90. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
  91. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
  92. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
  93. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
  94. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_select_star.py +0 -0
  95. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
  96. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
  97. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
  98. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
  99. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
  100. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
  101. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
  102. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
  103. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
  104. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
  105. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
  106. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
  107. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
  108. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
  109. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
  110. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
  111. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
  112. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
  113. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
  114. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
  115. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
  116. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
  117. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
  118. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
  119. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
  120. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
  121. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
  122. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
  123. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
  124. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
  125. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
  126. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
  127. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
  128. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
  129. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
  130. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
  131. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
  132. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
  133. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
  134. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
  135. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
  136. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
  137. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
  138. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
  139. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
  140. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
  141. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
  142. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
  143. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
  144. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
  145. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
  146. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
  147. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
  148. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
  149. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
  150. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
  151. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
  152. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
  153. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
  154. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
  155. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
  156. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
  157. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/stepdown.py +0 -0
  158. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
  159. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
  160. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
  161. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
  162. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
  163. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
  164. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
  165. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
  166. {sarj_python_lint-1.9.0 → sarj_python_lint-1.9.1}/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.9.0
3
+ Version: 1.9.1
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.9.0"
3
+ version = "1.9.1"
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" }]
@@ -39,6 +39,7 @@ from sarj_python_lint.rules.no_copied_inherited_docstring import NoCopiedInherit
39
39
  from sarj_python_lint.rules.no_cors_wildcard_with_credentials import (
40
40
  NoCorsWildcardWithCredentials,
41
41
  )
42
+ from sarj_python_lint.rules.no_database_programmability import NoDatabaseProgrammability
42
43
  from sarj_python_lint.rules.no_delete_statement import NoDeleteStatement
43
44
  from sarj_python_lint.rules.no_dunder_all import NoDunderAll
44
45
  from sarj_python_lint.rules.no_duplicate_dunder_all_entry import NoDuplicateDunderAllEntry
@@ -193,6 +194,7 @@ if TYPE_CHECKING:
193
194
 
194
195
  REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
195
196
  {
197
+ NoDatabaseProgrammability.id: NoDatabaseProgrammability,
196
198
  RequireExplicitContractImplementation.id: RequireExplicitContractImplementation,
197
199
  DiscourageNullableConstructorParameters.id: DiscourageNullableConstructorParameters,
198
200
  RequirePublicDependencyContract.id: RequirePublicDependencyContract,
@@ -85,13 +85,27 @@ def _sql_noise_end(text: str, start: int, *, mask_dollar_quotes: bool) -> int |
85
85
  end = text.find("\n", start)
86
86
  return len(text) if end == -1 else end
87
87
  if text.startswith("/*", start):
88
- end = text.find("*/", start + 2)
89
- return len(text) if end == -1 else end + 2
88
+ return _block_comment_end(text, start)
90
89
  if mask_dollar_quotes and ch == "$":
91
90
  return _dollar_quote_end(text, start)
92
91
  return None
93
92
 
94
93
 
94
+ def _block_comment_end(text: str, start: int) -> int:
95
+ depth = 1
96
+ i = start + 2
97
+ while i < len(text) and depth:
98
+ if text.startswith("/*", i):
99
+ depth += 1
100
+ i += 2
101
+ elif text.startswith("*/", i):
102
+ depth -= 1
103
+ i += 2
104
+ else:
105
+ i += 1
106
+ return i
107
+
108
+
95
109
  def _dollar_quote_end(text: str, start: int) -> int | None:
96
110
  delimiter_end = text.find("$", start + 1)
97
111
  if delimiter_end == -1:
@@ -0,0 +1,118 @@
1
+ from __future__ import annotations
2
+
3
+ import ast
4
+ from pathlib import PurePosixPath
5
+ import re
6
+ from typing import TYPE_CHECKING, ClassVar, final, override
7
+
8
+ from sarj_python_lint.rule_base import (
9
+ AutofixPolicy,
10
+ Diagnostic,
11
+ ExampleFile,
12
+ ExampleOutcome,
13
+ Rule,
14
+ RuleCategory,
15
+ RuleDocumentation,
16
+ RuleExample,
17
+ Severity,
18
+ is_suppressed,
19
+ )
20
+ from sarj_python_lint.rules._sql import sql_string_value, strip_sql_noise
21
+
22
+
23
+ if TYPE_CHECKING:
24
+ from sarj_python_lint._file_context import PythonFileContext
25
+
26
+
27
+ _DATABASE_BEHAVIOR = re.compile(
28
+ r"(?:\A|;)\s*(?:CREATE\s+(?:OR\s+REPLACE\s+)?(?:FUNCTION|TRIGGER)|"
29
+ r"CREATE\s+CONSTRAINT\s+TRIGGER|ALTER\s+TABLE\s+[^;]+?\s+ENABLE\s+(?:(?:ALWAYS|REPLICA)\s+)?TRIGGER)\b",
30
+ re.IGNORECASE,
31
+ )
32
+ _BAD = 'await conn.execute("CREATE TRIGGER fail_counter BEFORE UPDATE ON batch EXECUTE FUNCTION fail_counter()")\n'
33
+ _GOOD = 'await conn.execute("ALTER TABLE batch ADD CONSTRAINT fail_counter CHECK (false) NOT VALID")\n'
34
+
35
+
36
+ @final
37
+ class NoDatabaseProgrammability(Rule):
38
+ id = "no-database-programmability"
39
+ code = "SARJ470"
40
+ documentation: ClassVar[RuleDocumentation | None] = RuleDocumentation(
41
+ default_level=Severity.WARNING,
42
+ summary="Keep stored SQL functions and triggers out of embedded SQL.",
43
+ rationale="SQL hidden in Python can bypass migration-only architecture rules. Procedural fault fixtures require extra database objects and cleanup when a declarative constraint suffices.",
44
+ remediation="Use declarative constraints or explicit transactional application behavior, including in test fixtures. Use an exact SARJ470 suppression for an approved compatibility exception.",
45
+ category=RuleCategory.ARCHITECTURE,
46
+ autofix=AutofixPolicy.NONE,
47
+ limitations=(
48
+ "Checks inline execute/executemany SQL and import-proven psycopg.sql.SQL constructors, including aliases, concatenation, f-strings, and format calls. Test fixtures are included; generated Python is excluded.",
49
+ "Reports CREATE FUNCTION, CREATE OR REPLACE FUNCTION, CREATE TRIGGER, CREATE CONSTRAINT TRIGGER, and ALTER TABLE ENABLE TRIGGER. Existing function calls, DROP, and DISABLE remain valid.",
50
+ "SQL comments, quoted values, and dollar-quoted bodies are masked. Standalone example strings and Python functions are not SQL execution.",
51
+ "Runtime builders, SQL passed through named variables without SQL constructors, and dynamically generated DDL inside procedural bodies are not inferred. Non-PostgreSQL execution APIs are not distinguished.",
52
+ ),
53
+ examples=(
54
+ RuleExample(
55
+ example_id="trigger-fault",
56
+ title="Procedural fault fixture",
57
+ outcome=ExampleOutcome.MATCH,
58
+ files=(ExampleFile.python("tests/fakes/batch_fault.py", _BAD),),
59
+ focus_path=PurePosixPath("tests/fakes/batch_fault.py"),
60
+ expected_count=1,
61
+ public=True,
62
+ ),
63
+ RuleExample(
64
+ example_id="constraint-fault",
65
+ title="Declarative failure in a disposable database",
66
+ outcome=ExampleOutcome.NO_MATCH,
67
+ files=(ExampleFile.python("tests/fakes/batch_fault.py", _GOOD),),
68
+ focus_path=PurePosixPath("tests/fakes/batch_fault.py"),
69
+ expected_count=0,
70
+ public=True,
71
+ ),
72
+ ),
73
+ )
74
+ description = documentation.summary
75
+
76
+ @override
77
+ def check_context(self, context: PythonFileContext) -> list[Diagnostic]:
78
+ if context.generated or context.tree is None:
79
+ return []
80
+ diagnostics: list[Diagnostic] = []
81
+ for node in context.nodes(ast.Call):
82
+ query = _query_expression(node, context)
83
+ value = _literal_sql(query) if query is not None else None
84
+ if value is None or _DATABASE_BEHAVIOR.search(strip_sql_noise(value)) is None:
85
+ continue
86
+ if is_suppressed(context.source_lines, node.lineno, self.code):
87
+ continue
88
+ diagnostics.append(
89
+ Diagnostic(
90
+ path=context.path,
91
+ line=node.lineno,
92
+ col=node.col_offset + 1,
93
+ code=self.code,
94
+ severity=Severity.WARNING,
95
+ message="Stored SQL functions and triggers are prohibited by project architecture, including in test fixtures; use a declarative constraint or explicit transactional application code.",
96
+ )
97
+ )
98
+ return sorted(diagnostics, key=lambda item: (item.line, item.col))
99
+
100
+
101
+ def _query_expression(node: ast.Call, context: PythonFileContext) -> ast.expr | None:
102
+ if context.imports.resolves(node.func, sources=frozenset({"psycopg.sql"}), symbol="SQL") or (
103
+ isinstance(node.func, ast.Attribute)
104
+ and node.func.attr == "SQL"
105
+ and context.imports.resolves(node.func.value, sources=frozenset({"psycopg"}), symbol="sql")
106
+ ):
107
+ return node.args[0] if node.args else None
108
+ if not isinstance(node.func, ast.Attribute) or node.func.attr not in {"execute", "executemany"}:
109
+ return None
110
+ if node.args:
111
+ return node.args[0]
112
+ return next((keyword.value for keyword in node.keywords if keyword.arg == "query"), None)
113
+
114
+
115
+ def _literal_sql(node: ast.expr) -> str | None:
116
+ if isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute) and node.func.attr == "format":
117
+ return _literal_sql(node.func.value)
118
+ return sql_string_value(node)