sarj-python-lint 1.7.8__tar.gz → 1.8.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 (164) hide show
  1. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/PKG-INFO +1 -1
  2. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/pyproject.toml +1 -1
  3. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +58 -57
  4. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +7 -3
  5. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/.gitignore +0 -0
  6. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/LICENSE +0 -0
  7. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/README.md +0 -0
  8. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/__init__.py +0 -0
  9. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/__main__.py +0 -0
  10. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/_analysis_session.py +0 -0
  11. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/_file_context.py +0 -0
  12. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/_filesystem.py +0 -0
  13. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/_python_target.py +0 -0
  14. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/_ratchet_cli.py +0 -0
  15. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/_secret_names.py +0 -0
  16. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/_version.py +0 -0
  17. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/benchmark.py +0 -0
  18. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/json_boundary.py +0 -0
  19. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/py.typed +0 -0
  20. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/ratchet.py +0 -0
  21. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rule_base.py +0 -0
  22. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/__init__.py +0 -0
  23. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
  24. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_ast_index.py +0 -0
  25. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_ast_position.py +0 -0
  26. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_comments.py +0 -0
  27. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
  28. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
  29. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_docstrings.py +0 -0
  30. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_fastapi.py +0 -0
  31. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_first_party.py +0 -0
  32. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
  33. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_imports.py +0 -0
  34. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_logging.py +0 -0
  35. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
  36. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_paths.py +0 -0
  37. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_project_index.py +0 -0
  38. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
  39. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_pytest.py +0 -0
  40. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_registry.py +0 -0
  41. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_resource_provenance.py +0 -0
  42. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_set_fields.py +0 -0
  43. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_sql.py +0 -0
  44. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
  45. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
  46. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/_test_composition.py +0 -0
  47. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
  48. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
  49. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
  50. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/discourage_nullable_constructor_parameters.py +0 -0
  51. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
  52. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
  53. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
  54. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
  55. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
  56. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
  57. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
  58. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
  59. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
  60. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
  61. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
  62. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
  63. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
  64. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
  65. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
  66. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
  67. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
  68. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
  69. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
  70. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
  71. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
  72. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
  73. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
  74. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
  75. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
  76. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
  77. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
  78. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
  79. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
  80. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
  81. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
  82. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
  83. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
  84. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
  85. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
  86. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
  87. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
  88. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
  89. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
  90. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
  91. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
  92. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_select_star.py +0 -0
  93. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
  94. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
  95. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
  96. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
  97. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
  98. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
  99. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
  100. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
  101. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
  102. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
  103. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
  104. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
  105. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
  106. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
  107. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
  108. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
  109. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
  110. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
  111. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
  112. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
  113. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
  114. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
  115. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
  116. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
  117. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
  118. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
  119. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
  120. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
  121. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
  122. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
  123. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
  124. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
  125. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
  126. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
  127. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
  128. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
  129. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
  130. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
  131. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
  132. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
  133. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
  134. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
  135. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
  136. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
  137. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
  138. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
  139. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
  140. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
  141. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
  142. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
  143. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
  144. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
  145. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
  146. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
  147. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
  148. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
  149. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
  150. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
  151. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
  152. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
  153. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
  154. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
  155. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/stepdown.py +0 -0
  156. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
  157. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
  158. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
  159. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
  160. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
  161. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
  162. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
  163. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.0}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
  164. {sarj_python_lint-1.7.8 → sarj_python_lint-1.8.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.7.8
3
+ Version: 1.8.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.7.8"
3
+ version = "1.8.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" }]
@@ -243,26 +243,12 @@ def _expression_args(node: exp.Expr, key: str) -> tuple[exp.Expr, ...]:
243
243
  return tuple(child for child in node.iter_expressions() if child.arg_key == key)
244
244
 
245
245
 
246
- def _root_selects(query: exp.Query) -> tuple[exp.Select, ...]:
247
- from sqlglot import exp # ruff: ignore[import-outside-top-level] -- keep parser startup off unrelated lint runs
248
-
249
- if isinstance(query, exp.Select):
250
- return (query,)
251
- if not isinstance(query, exp.SetOperation):
252
- return ()
253
- branches: list[exp.Select] = []
254
- for key in ("this", "expression"):
255
- if isinstance((branch := _expression_arg(query, key)), exp.Query):
256
- branches.extend(_root_selects(branch))
257
- return tuple(branches)
258
-
259
-
260
246
  def _derived_query(node: exp.Expr | None) -> exp.Query | None:
261
247
  from sqlglot import exp # ruff: ignore[import-outside-top-level] -- keep parser startup off unrelated lint runs
262
248
 
263
- if isinstance(node, exp.Subquery) and isinstance((query := _expression_arg(node, "this")), exp.Query):
264
- return query
265
- return None
249
+ while isinstance(node, (exp.Lateral, exp.Paren, exp.Subquery)):
250
+ node = _expression_arg(node, "this")
251
+ return node if isinstance(node, exp.Query) else None
266
252
 
267
253
 
268
254
  @final
@@ -278,18 +264,21 @@ class ComplexPostgresQueryRequiresArchitectureReview(Rule):
278
264
  "cost, a bad data model, or the right datastore."
279
265
  ),
280
266
  remediation=(
281
- "Review whether the schema exposes the operational fact directly, together with production-like "
282
- "cardinality and EXPLAIN output. Simplify the query or read model when warranted, but do not mechanically "
283
- "replace database joins with application joins. Keep atomic coordination in one statement; consider a "
284
- "columnar store only for measured repeated analytical reads with an explicit freshness contract."
267
+ "Prefer ClickHouse for reporting, historical analysis, and broad aggregations, with measured performance "
268
+ "and an explicit freshness contract. For transactional work, simplify the query or document why "
269
+ "PostgreSQL is required, with query bounds and production-like EXPLAIN evidence. Review whether the "
270
+ "schema exposes the operational fact directly; preserve atomic coordination and do not mechanically "
271
+ "replace database joins with application joins. Do not add reporting-specific indexes without "
272
+ "evaluating ClickHouse."
285
273
  ),
286
274
  category=RuleCategory.ARCHITECTURE,
287
275
  autofix=AutofixPolicy.NONE,
288
276
  limitations=(
289
- "Only statically recoverable SQL passed to execute, executemany, fetch, fetchrow, fetchval, or prepare in non-test modules with explicit PostgreSQL imports is analyzed; extended derived, staged, and wide-shape review requires a recognized store module.",
277
+ "Only statically recoverable SQL passed to execute, executemany, fetch, fetchrow, fetchval, or prepare in non-test modules with explicit PostgreSQL imports is analyzed, regardless of module filename.",
290
278
  "Execution receivers must use a conventional database name; custom wrappers and dynamically obtained receivers abstain.",
291
279
  "One direct, unambiguous simple-name binding in the same lexical scope is followed; standalone constants, branches, aliases, attributes, containers, wildcard imports, and cross-scope flow abstain.",
292
- "A query is reviewed for a complex derived relation, at least four joins in one query block, at least six nested query stages, three joins plus four stages, or 25 projections plus two joins.",
280
+ "Review covers derived relations in every SELECT (including LATERAL, CTEs, predicates and writes), four explicit or comma joins, six query stages, three joins plus four stages, or 25 projections plus two joins.",
281
+ "Derived-relation review requires grouping, HAVING or windowing combined with joins or nested reads; expression density is not a separate trigger.",
293
282
  "Projection width alone is accepted, and UNION branches are measured independently; execution plans and production cardinality remain authoritative.",
294
283
  "ClickHouse and BigQuery syntax is excluded per query, including in mixed-backend modules.",
295
284
  "The rule does not infer optimizer behavior, materialization, performance, data-model quality, or datastore placement.",
@@ -405,9 +394,11 @@ class ComplexPostgresQueryRequiresArchitectureReview(Rule):
405
394
  code=self.code,
406
395
  severity=Severity.WARNING,
407
396
  message=(
408
- f"{signal} requires architecture review;{guidance} "
409
- "Review warning only, not a defect or cost claim; syntax does not prove a bad data model or "
410
- "an offload decision."
397
+ f"Complex PostgreSQL query: {signal}. Architecture review required;{guidance} "
398
+ "Prefer ClickHouse for reporting, historical analysis, and broad aggregations. "
399
+ "For transactional work, simplify the query or document why PostgreSQL is required, "
400
+ "with query bounds and EXPLAIN evidence. Do not add reporting-specific indexes without "
401
+ "evaluating ClickHouse. Syntax alone does not establish runtime cost or datastore placement."
411
402
  ),
412
403
  )
413
404
  )
@@ -494,45 +485,40 @@ def _query_architecture_signal(text_value: str, path: Path) -> str | None:
494
485
  or (is_store_module(path) and analytical_signal(sql_without_noise) is not None)
495
486
  ):
496
487
  return None
497
- return _parse_signal(text_value, include_extended_shapes=is_store_module(path))
488
+ return _parse_signal(text_value)
498
489
 
499
490
 
500
- def _parse_signal(sql: str, *, include_extended_shapes: bool) -> str | None:
491
+ def _parse_signal(sql: str) -> str | None:
501
492
  import sqlglot # ruff: ignore[import-outside-top-level] -- parse only SQL that passes cheap ownership and shape gates
502
493
  from sqlglot.errors import SqlglotError # ruff: ignore[import-outside-top-level] -- paired with lazy parser import
503
494
 
504
495
  normalized = _COMPOSABLE_HOLE.sub(_SQL_HOLE, sql)
505
496
  try:
506
- statements = sqlglot.parse(normalized, read="postgres")
497
+ for statement in sqlglot.parse(normalized, read="postgres"):
498
+ if statement is not None and (signal := _architecture_signal(statement)) is not None:
499
+ return signal
507
500
  except SqlglotError:
508
501
  return None
509
- for statement in statements:
510
- if (
511
- statement is not None
512
- and (signal := _architecture_signal(statement, include_extended_shapes=include_extended_shapes)) is not None
513
- ):
514
- return signal
515
502
  return None
516
503
 
517
504
 
518
- def _architecture_signal(statement: exp.Expr, *, include_extended_shapes: bool) -> str | None:
505
+ def _architecture_signal(statement: exp.Expr) -> str | None:
519
506
  from sqlglot import exp # ruff: ignore[import-outside-top-level] -- keep parser startup off unrelated lint runs
520
507
 
521
- if not isinstance(statement, exp.Query):
508
+ if not isinstance(statement, (exp.Query, exp.Insert, exp.Update, exp.Delete)):
522
509
  return None
523
510
  maximum_joins = _maximum_query_joins(statement)
524
511
  if maximum_joins >= _JOIN_LIMIT:
525
- return f"SELECT block has {maximum_joins} JOINs (4+ explicit JOINs)"
526
- if not include_extended_shapes:
527
- return None
528
- nested_queries = sum(1 for query in statement.walk() if isinstance(query, exp.Query)) - 1
512
+ return f"SELECT block has {maximum_joins} JOINs (4+ explicit JOINs or comma relations)"
513
+ nested_queries = sum(1 for query in statement.walk() if isinstance(query, (exp.Select, exp.SetOperation)))
514
+ nested_queries -= isinstance(statement, (exp.Select, exp.SetOperation))
529
515
  if (derived_signal := _derived_architecture_signal(statement)) is not None:
530
516
  return derived_signal
531
517
  if nested_queries >= _STAGE_LIMIT:
532
518
  return f"Query has {nested_queries} CTE/subquery stages"
533
519
  if maximum_joins >= _COMBINED_JOIN_LIMIT and nested_queries >= _COMBINED_STAGE_LIMIT:
534
520
  return f"Query combines {maximum_joins} JOINs with {nested_queries} CTE/subquery stages"
535
- for select in _root_selects(statement):
521
+ for select in statement.find_all(exp.Select):
536
522
  projections = tuple(_expression_args(select, "expressions"))
537
523
  joins = tuple(join for join in _expression_args(select, "joins") if isinstance(join, exp.Join))
538
524
  if len(projections) >= _WIDE_PROJECTION_LIMIT and len(joins) >= _WIDE_PROJECTION_JOIN_LIMIT:
@@ -540,20 +526,20 @@ def _architecture_signal(statement: exp.Expr, *, include_extended_shapes: bool)
540
526
  return None
541
527
 
542
528
 
543
- def _derived_architecture_signal(statement: exp.Query) -> str | None:
529
+ def _derived_architecture_signal(statement: exp.Expr) -> str | None:
544
530
  from sqlglot import exp # ruff: ignore[import-outside-top-level] -- keep parser startup off unrelated lint runs
545
531
 
546
- for select in _root_selects(statement):
532
+ for select in statement.find_all(exp.Select):
547
533
  for join in _expression_args(select, "joins"):
548
534
  if not isinstance(join, exp.Join):
549
535
  continue
550
536
  if (derived := _derived_query(_expression_arg(join, "this"))) is not None and _has_complex_body(derived):
551
- return "Complex JOIN-derived query"
537
+ return "JOIN-derived query combines grouping, HAVING or windowing with joins or nested reads"
552
538
  from_clause = _expression_arg(select, "from_")
553
539
  if not isinstance(from_clause, exp.From):
554
540
  continue
555
541
  if (derived := _derived_query(_expression_arg(from_clause, "this"))) is not None and _has_complex_body(derived):
556
- return "Complex FROM-derived query"
542
+ return "FROM-derived query combines grouping, HAVING or windowing with joins or nested reads"
557
543
  return None
558
544
 
559
545
 
@@ -561,28 +547,43 @@ def _has_complex_body(query: exp.Query) -> bool:
561
547
  from sqlglot import exp # ruff: ignore[import-outside-top-level] -- keep parser startup off unrelated lint runs
562
548
 
563
549
  if isinstance(query, exp.SetOperation):
564
- return True
550
+ return any(
551
+ isinstance(branch := _expression_arg(query, key), exp.Query)
552
+ and (_has_row_transform(branch) or _has_complex_body(branch))
553
+ for key in ("this", "expression")
554
+ )
565
555
  if not isinstance(query, exp.Select):
566
556
  return False
567
- if _expression_arg(query, "group") is not None or _expression_arg(query, "having") is not None:
568
- return True
569
- if any(window.parent_select is query for window in query.find_all(exp.Window)):
570
- return True
571
- from_clause = _expression_arg(query, "from_")
572
- if isinstance(from_clause, exp.From) and _derived_query(_expression_arg(from_clause, "this")) is not None:
557
+ if _has_row_transform(query) and (
558
+ _expression_args(query, "joins") or any(select is not query for select in query.find_all(exp.Select))
559
+ ):
573
560
  return True
574
- for join in _expression_args(query, "joins"):
575
- if isinstance(join, exp.Join) and _derived_query(_expression_arg(join, "this")) is not None:
561
+ for relation in query.find_all(exp.From, exp.Join):
562
+ if relation.parent is not query:
563
+ continue
564
+ child = _derived_query(_expression_arg(relation, "this"))
565
+ if child is not None and (_has_row_transform(child) or _has_complex_body(child)):
576
566
  return True
577
567
  return False
578
568
 
579
569
 
580
- def _maximum_query_joins(statement: exp.Query) -> int:
570
+ def _has_row_transform(query: exp.Query) -> bool:
571
+ from sqlglot import exp # ruff: ignore[import-outside-top-level] -- keep parser startup off unrelated lint runs
572
+
573
+ select = _derived_query(query)
574
+ return isinstance(select, exp.Select) and (
575
+ _expression_arg(select, "group") is not None
576
+ or _expression_arg(select, "having") is not None
577
+ or any(window.parent_select is select for window in select.find_all(exp.Window))
578
+ )
579
+
580
+
581
+ def _maximum_query_joins(statement: exp.Expr) -> int:
581
582
  from sqlglot import exp # ruff: ignore[import-outside-top-level] -- keep parser startup off unrelated lint runs
582
583
 
583
584
  return max(
584
585
  (
585
- sum(isinstance(join, exp.Join) and "pivots" in join.args for join in _expression_args(select, "joins"))
586
+ sum(isinstance(join, exp.Join) for join in _expression_args(select, "joins"))
586
587
  for select in statement.find_all(exp.Select)
587
588
  ),
588
589
  default=0,
@@ -159,8 +159,10 @@ class NoAnalyticalAggregationInPostgresStore(Rule):
159
159
  "consistent operational aggregates remain valid PostgreSQL work."
160
160
  ),
161
161
  remediation=(
162
- "Review the query bounds and execution plan. Move reporting scans to the repository's "
163
- "columnar store, or document why the aggregate must remain transactional."
162
+ "Review the query bounds and execution plan. Prefer ClickHouse for reporting, historical analysis, "
163
+ "and broad aggregations, with measured performance and an explicit freshness contract. "
164
+ "For PostgreSQL, document why the aggregate must remain transactional, with EXPLAIN evidence. "
165
+ "Do not add reporting-specific indexes without evaluating ClickHouse."
164
166
  ),
165
167
  category=RuleCategory.ARCHITECTURE,
166
168
  autofix=AutofixPolicy.NONE,
@@ -247,7 +249,9 @@ class NoAnalyticalAggregationInPostgresStore(Rule):
247
249
  severity=Severity.WARNING,
248
250
  message=(
249
251
  f"Possible analytical PostgreSQL query ({signal}); review its bounds and execution plan. "
250
- "Move reporting scans to the columnar store, or document why this aggregate must remain transactional."
252
+ "Prefer ClickHouse for reporting, historical analysis, and broad aggregations. "
253
+ "For PostgreSQL, document why this aggregate must remain transactional, with EXPLAIN evidence. "
254
+ "Do not add reporting-specific indexes without evaluating ClickHouse."
251
255
  ),
252
256
  )
253
257
  )