sarj-python-lint 1.83.1__tar.gz → 1.85.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 (301) hide show
  1. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/PKG-INFO +1 -1
  2. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/pyproject.toml +1 -1
  3. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/__main__.py +3 -2
  4. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_autofix.py +9 -4
  5. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_file_context.py +2 -0
  6. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_annotation_origins.py +43 -0
  7. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_annotation_semantics.py +7 -4
  8. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_copy_on_write.py +29 -20
  9. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_docstrings.py +7 -4
  10. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_fastapi.py +7 -4
  11. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_imports.py +7 -4
  12. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_mapping_shape_callbacks.py +18 -12
  13. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_native_object_contracts.py +25 -16
  14. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_opaque_contracts.py +22 -15
  15. sarj_python_lint-1.85.0/src/sarj_python_lint/rules/_postgres.py +17 -0
  16. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_project_index.py +7 -4
  17. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_prose_budget.py +7 -4
  18. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_registry.py +24 -0
  19. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_resource_provenance.py +7 -6
  20. sarj_python_lint-1.85.0/src/sarj_python_lint/rules/_store_sql.py +245 -0
  21. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_store_writes.py +8 -5
  22. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_test_composition.py +17 -14
  23. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +95 -44
  24. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +7 -4
  25. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +14 -8
  26. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/duplicate_closed_pure_helper.py +7 -4
  27. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_broad_keyword_capture.py +7 -4
  28. sarj_python_lint-1.85.0/src/sarj_python_lint/rules/no_custom_control_flow_exception.py +237 -0
  29. sarj_python_lint-1.85.0/src/sarj_python_lint/rules/no_discarded_tagged_outcome.py +469 -0
  30. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +48 -37
  31. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_invalid_pydantic_bypass.py +7 -4
  32. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +16 -10
  33. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +119 -58
  34. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +15 -9
  35. sarj_python_lint-1.85.0/src/sarj_python_lint/rules/no_recording_store.py +276 -0
  36. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_redundant_local_alias.py +7 -4
  37. sarj_python_lint-1.85.0/src/sarj_python_lint/rules/no_redundant_sql_alias.py +164 -0
  38. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_repeated_string_literal.py +8 -7
  39. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +18 -8
  40. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_tautological_expect.py +29 -17
  41. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +23 -20
  42. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/over_mocked_test.py +7 -6
  43. sarj_python_lint-1.85.0/src/sarj_python_lint/rules/prefer_application_model_joins.py +138 -0
  44. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_class_row.py +14 -10
  45. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +7 -4
  46. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +17 -11
  47. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_declarative_batch_validation.py +14 -8
  48. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_discriminated_model_union.py +7 -4
  49. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +7 -4
  50. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +7 -4
  51. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_library_fake.py +7 -4
  52. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +276 -9
  53. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +9 -4
  54. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_native_string_check.py +14 -8
  55. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +14 -11
  56. sarj_python_lint-1.85.0/src/sarj_python_lint/rules/prefer_optional_composite.py +297 -0
  57. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_or_pattern.py +7 -4
  58. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_pydantic_model_assertion.py +13 -10
  59. sarj_python_lint-1.85.0/src/sarj_python_lint/rules/prefer_runtime_validation.py +97 -0
  60. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +7 -4
  61. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +7 -4
  62. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_str_enum.py +45 -30
  63. sarj_python_lint-1.85.0/src/sarj_python_lint/rules/prefer_typed_cli.py +219 -0
  64. sarj_python_lint-1.85.0/src/sarj_python_lint/rules/prefer_typed_mapping_get.py +189 -0
  65. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +9 -6
  66. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +22 -19
  67. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/redundant_docstring.py +8 -7
  68. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +8 -5
  69. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +7 -4
  70. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +30 -21
  71. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/require_port_for_service.py +23 -16
  72. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +16 -10
  73. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/require_pydantic_for_mapping_shape.py +18 -20
  74. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +8 -5
  75. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/restated_test_docstring.py +16 -12
  76. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/stepdown.py +8 -5
  77. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +29 -17
  78. sarj_python_lint-1.85.0/src/sarj_python_lint/rules/store_insert_requires_explicit_conflict_policy.py +105 -0
  79. sarj_python_lint-1.85.0/src/sarj_python_lint/rules/store_mutation_command_fanout.py +172 -0
  80. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +7 -4
  81. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +30 -24
  82. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/unused_mock_setup.py +7 -4
  83. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/.gitignore +0 -0
  84. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/LICENSE +0 -0
  85. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/README.md +0 -0
  86. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/__init__.py +0 -0
  87. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_analysis_session.py +0 -0
  88. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_autofix_aliases.py +0 -0
  89. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_autofix_annotations.py +0 -0
  90. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_autofix_comments.py +0 -0
  91. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_autofix_declared_contracts.py +0 -0
  92. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_autofix_declared_tests.py +0 -0
  93. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_autofix_docs.py +0 -0
  94. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_autofix_enums.py +0 -0
  95. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_autofix_exhaustiveness.py +0 -0
  96. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_autofix_inert.py +0 -0
  97. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_autofix_parameters.py +0 -0
  98. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_autofix_repeated.py +0 -0
  99. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_autofix_security.py +0 -0
  100. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_autofix_sql_projection.py +0 -0
  101. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_autofix_tests.py +0 -0
  102. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_filesystem.py +0 -0
  103. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_fixes.py +0 -0
  104. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_parallel.py +0 -0
  105. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_python_target.py +0 -0
  106. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_ratchet_cli.py +0 -0
  107. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_secret_names.py +0 -0
  108. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_source.py +0 -0
  109. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/_version.py +0 -0
  110. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/application_logging.py +0 -0
  111. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/autofix.py +0 -0
  112. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/benchmark.py +0 -0
  113. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/cast_access.py +0 -0
  114. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/interpreter_argv.py +0 -0
  115. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/json_boundary.py +0 -0
  116. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/py.typed +0 -0
  117. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/ratchet.py +0 -0
  118. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rule_base.py +0 -0
  119. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/__init__.py +0 -0
  120. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_ast_index.py +0 -0
  121. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_ast_names.py +0 -0
  122. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_ast_position.py +0 -0
  123. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_async_lifecycle.py +0 -0
  124. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_binding_inventory.py +0 -0
  125. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_closed_helpers.py +0 -0
  126. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_comment_narration.py +0 -0
  127. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_comments.py +0 -0
  128. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
  129. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_data_union_discriminator.py +0 -0
  130. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_declared_contracts.py +0 -0
  131. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
  132. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_exports.py +0 -0
  133. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_first_party.py +0 -0
  134. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
  135. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_inert_model_annotation.py +0 -0
  136. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_inherited_contract.py +0 -0
  137. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_local_alias_fix.py +0 -0
  138. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_local_source.py +0 -0
  139. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_logging.py +0 -0
  140. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_namespace_access.py +0 -0
  141. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_native_namespace_capability.py +0 -0
  142. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
  143. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_numeric_proof.py +0 -0
  144. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_paths.py +0 -0
  145. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_pydantic_field_contract.py +0 -0
  146. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_pytest.py +0 -0
  147. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_resolved_operation_signature.py +0 -0
  148. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_route_shapes.py +0 -0
  149. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_runtime_bindings.py +0 -0
  150. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_service_http_callbacks.py +0 -0
  151. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_set_fields.py +0 -0
  152. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_sql.py +0 -0
  153. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_sql_aggregate_identity.py +0 -0
  154. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_sql_explain.py +0 -0
  155. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_sql_policy_source.py +0 -0
  156. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_static_json_alias.py +0 -0
  157. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
  158. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
  159. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_test_provenance.py +0 -0
  160. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_transactional_sql.py +0 -0
  161. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/_type_guard_symbols.py +0 -0
  162. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
  163. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/async_executor_shutdown.py +0 -0
  164. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
  165. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/async_subprocess_cancellation_cleanup.py +0 -0
  166. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/cancel_owned_task_without_join.py +0 -0
  167. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
  168. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/delete_route_returns_no_content.py +0 -0
  169. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/duplicate_adjacent_exception_handler.py +0 -0
  170. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/duplicate_pytest_fixture_params.py +0 -0
  171. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
  172. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/failed_probe_zero_threshold.py +0 -0
  173. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
  174. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
  175. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
  176. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
  177. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
  178. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
  179. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
  180. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/nan_hidden_by_builtin_min.py +0 -0
  181. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
  182. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
  183. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
  184. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_async_function_cache.py +0 -0
  185. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_before_validator_input_mutation.py +0 -0
  186. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_blanket_suppression.py +0 -0
  187. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
  188. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_conflicting_pydantic_alias_validation.py +0 -0
  189. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
  190. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_contradictory_type_guard.py +0 -0
  191. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
  192. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
  193. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_create_verb_in_post_path.py +0 -0
  194. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
  195. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_defaulted_environment_setting.py +0 -0
  196. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
  197. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_discarded_validation_result.py +0 -0
  198. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_dual_shape_model_field_reader.py +0 -0
  199. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
  200. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
  201. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_duplicate_field_validator_body.py +0 -0
  202. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_exception_text_in_500_detail.py +0 -0
  203. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
  204. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
  205. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
  206. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
  207. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
  208. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_fixed_sleep_in_loop.py +0 -0
  209. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_folded_bad_request_not_found.py +0 -0
  210. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
  211. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
  212. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_hasattr.py +0 -0
  213. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_impossible_pydantic_field_bounds.py +0 -0
  214. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
  215. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_input_model_mutation.py +0 -0
  216. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_interpreter_source_arguments.py +0 -0
  217. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
  218. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_invalid_pydantic_validator_return.py +0 -0
  219. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_loguru_exc_info.py +0 -0
  220. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_loguru_printf_format.py +0 -0
  221. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_mocked_pydantic_value_object.py +0 -0
  222. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_mutating_container_transform.py +0 -0
  223. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
  224. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
  225. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
  226. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_partial_type_is.py +0 -0
  227. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
  228. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
  229. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
  230. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
  231. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
  232. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
  233. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
  234. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_select_star.py +0 -0
  235. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
  236. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_shallow_container_type_guard.py +0 -0
  237. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_shallow_record_type_guard.py +0 -0
  238. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_slash_collection_root_under_prefix.py +0 -0
  239. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
  240. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_test_method_grafting.py +0 -0
  241. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
  242. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
  243. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_unknown_pydantic_validator_field.py +0 -0
  244. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_unused_copy_result.py +0 -0
  245. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_unused_underscored_keyword_parameter.py +0 -0
  246. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
  247. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
  248. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
  249. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/no_work_verb_get_route.py +0 -0
  250. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
  251. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
  252. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_autospec_for_callable_mock.py +0 -0
  253. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_bulk_upsert.py +0 -0
  254. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_direct_import_module.py +0 -0
  255. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_grouped_settings.py +0 -0
  256. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
  257. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
  258. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
  259. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
  260. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
  261. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
  262. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
  263. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
  264. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_precise_object_contract.py +0 -0
  265. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_psycopg_executemany.py +0 -0
  266. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
  267. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_pytest_dependency_fixtures.py +0 -0
  268. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_pytest_fixture_injection.py +0 -0
  269. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
  270. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_required_constructor_parameters.py +0 -0
  271. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
  272. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
  273. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_typed_settings_field.py +0 -0
  274. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
  275. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
  276. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
  277. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
  278. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/pytest_fixture_returns_closed_resource.py +0 -0
  279. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
  280. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
  281. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/redundant_test_constructor_forwarder.py +0 -0
  282. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
  283. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
  284. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/repeated_unimplemented_test_contract.py +0 -0
  285. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/require_exp_on_jwt_decode.py +0 -0
  286. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
  287. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/require_injectable_retry_sleep.py +0 -0
  288. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
  289. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
  290. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/require_precise_factory_signature.py +0 -0
  291. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
  292. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
  293. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
  294. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/store_field_update_reuses_upsert.py +0 -0
  295. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
  296. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
  297. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/test_without_explicit_oracle.py +0 -0
  298. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
  299. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
  300. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/rules/unused_test_factory_option.py +0 -0
  301. {sarj_python_lint-1.83.1 → sarj_python_lint-1.85.0}/src/sarj_python_lint/suppression_policy.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sarj-python-lint
3
- Version: 1.83.1
3
+ Version: 1.85.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.83.1"
3
+ version = "1.85.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" }]
@@ -187,12 +187,13 @@ _DIAGNOSTIC_PRECEDENCE = MappingProxyType(
187
187
  # A proven external or nested request boundary already requires schema
188
188
  # validation at this field read. Retain standalone shape advice when
189
189
  # that more specific rule is unselected or locally suppressed.
190
- "SARJ411": frozenset({"SARJ521"}),
190
+ "SARJ411": frozenset({"SARJ521", "SARJ535"}),
191
191
  # Any-valued mappings are the stronger correctness failure. SARJ008
192
192
  # still owns every fixed dictionary return when selected alone, while
193
193
  # an all-rules run emits one actionable diagnostic for this overlap.
194
194
  "SARJ447": frozenset({"SARJ008"}),
195
- "SARJ450": frozenset({"SARJ521"}),
195
+ "SARJ450": frozenset({"SARJ521", "SARJ535"}),
196
+ "SARJ521": frozenset({"SARJ535"}),
196
197
  "SARJ457": frozenset({"SARJ045"}),
197
198
  "SARJ465": frozenset({"SARJ071"}),
198
199
  "SARJ491": frozenset({"SARJ400"}),
@@ -759,6 +759,8 @@ def _primitive_set(node: ast.expr) -> bool:
759
759
 
760
760
 
761
761
  def _dispatch_fix(source: _Source, diagnostic: Diagnostic) -> list[TextEdit]:
762
+ if diagnostic.code == "SARJ080" and diagnostic.severity is not Severity.ERROR:
763
+ return []
762
764
  for root in source.context.nodes(ast.If):
763
765
  if not _at(root, diagnostic) or source.comments_between(root.lineno, _last_line(root)):
764
766
  continue
@@ -993,10 +995,13 @@ def _jwt_required_sequence(required: ast.expr) -> ast.expr | None:
993
995
  ):
994
996
  return None
995
997
  elements = [*required.elts, ast.Constant(value="exp")]
996
- if isinstance(required, ast.List):
997
- return ast.List(elts=elements, ctx=ast.Load())
998
- if isinstance(required, ast.Tuple):
999
- return ast.Tuple(elts=elements, ctx=ast.Load())
998
+ match required:
999
+ case ast.List():
1000
+ return ast.List(elts=elements, ctx=ast.Load())
1001
+ case ast.Tuple():
1002
+ return ast.Tuple(elts=elements, ctx=ast.Load())
1003
+ case _:
1004
+ pass
1000
1005
  return ast.Set(elts=elements)
1001
1006
 
1002
1007
 
@@ -22,6 +22,7 @@ if TYPE_CHECKING:
22
22
 
23
23
  from sarj_python_lint.rules._copy_on_write import CopyOnWriteAnalysis
24
24
  from sarj_python_lint.rules._resource_provenance import ResourceProvenance
25
+ from sarj_python_lint.rules._store_sql import StoreQuery
25
26
  from sarj_python_lint.rules._test_provenance import TestProvenance
26
27
 
27
28
 
@@ -31,6 +32,7 @@ class PythonFileContext:
31
32
  self.path = path
32
33
  self.source = source
33
34
  self.session = session if session is not None else AnalysisSession()
35
+ self.store_queries: tuple[StoreQuery, ...] | None = None
34
36
 
35
37
  @cached_property
36
38
  def symbol_source(self) -> str:
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import ast
4
+ from dataclasses import dataclass
4
5
  from typing import TYPE_CHECKING
5
6
 
6
7
  from sarj_python_lint.rules._imports import ImportIndex
@@ -10,6 +11,15 @@ if TYPE_CHECKING:
10
11
  from sarj_python_lint._file_context import PythonFileContext
11
12
 
12
13
 
14
+ _MAX_ALIAS_DEPTH = 32
15
+
16
+
17
+ @dataclass(frozen=True, slots=True)
18
+ class ResolvedAnnotation:
19
+ expression: ast.expr
20
+ at: ast.expr
21
+
22
+
13
23
  class AnnotationOrigins:
14
24
  def __init__(self, context: PythonFileContext) -> None:
15
25
  self.context = context
@@ -19,6 +29,39 @@ class AnnotationOrigins:
19
29
  for node in context.nodes(ast.Import, ast.ImportFrom)
20
30
  }
21
31
 
32
+ def alias_value(self, node: ast.Name, at: ast.AST) -> ast.expr | None:
33
+ value = self.context.annotation_semantics.aliases.get(node.id)
34
+ if value is None:
35
+ return None
36
+ binding = self.provenance.binding(node.id, at)
37
+ if not isinstance(binding, ast.Name):
38
+ return None
39
+ parent = self.context.parents.get(binding)
40
+ if isinstance(parent, ast.TypeAlias | ast.AnnAssign) and parent.value is value:
41
+ return value
42
+ return None
43
+
44
+ def resolve_annotation(self, node: ast.expr, *, at: ast.expr | None = None) -> ResolvedAnnotation | None:
45
+ current = node
46
+ scope = node if at is None else at
47
+ seen: set[str] = set()
48
+ for _ in range(_MAX_ALIAS_DEPTH):
49
+ parsed = self.context.annotation_semantics.parse(current)
50
+ if parsed is None:
51
+ return None
52
+ if isinstance(parsed, ast.Name):
53
+ if self.shadowed(parsed.id, scope):
54
+ return None
55
+ value = self.alias_value(parsed, scope)
56
+ if value is not None:
57
+ if parsed.id in seen:
58
+ return None
59
+ seen.add(parsed.id)
60
+ current = scope = value
61
+ continue
62
+ return ResolvedAnnotation(parsed, scope)
63
+ return None
64
+
22
65
  def shadowed(self, name: str, at: ast.AST) -> bool:
23
66
  return type_parameter_shadow(name, at, self.context)
24
67
 
@@ -158,10 +158,13 @@ def _bound_names(statement: ast.stmt) -> set[str]:
158
158
 
159
159
 
160
160
  def _target_names(node: ast.expr) -> set[str]:
161
- if isinstance(node, ast.Name):
162
- return {node.id}
163
- if isinstance(node, (ast.List, ast.Tuple)):
164
- return {name for item in node.elts for name in _target_names(item)}
161
+ match node:
162
+ case ast.Name():
163
+ return {node.id}
164
+ case ast.List() | ast.Tuple():
165
+ return {name for item in node.elts for name in _target_names(item)}
166
+ case _:
167
+ pass
165
168
  return set()
166
169
 
167
170
 
@@ -659,28 +659,37 @@ class CopyOnWriteAnalysis:
659
659
  return _Value(_Shape("dict", element=element), shared_children=shared, fields=tuple(fields.items()))
660
660
 
661
661
  def _write(self, target: ast.expr, bindings: Mapping[str, _Value]) -> None:
662
- if isinstance(target, (ast.Tuple, ast.List)):
663
- for item in target.elts:
664
- self._write(item, bindings)
665
- elif isinstance(target, (ast.Attribute, ast.Subscript)):
666
- owner = self._value(target.value, bindings)
667
- if (
668
- owner is not None
669
- and owner.borrowed
670
- and (
671
- isinstance(target, ast.Subscript)
672
- or (owner.shape.model is not None and self.models.field(owner.shape.model, target.attr) is not None)
673
- )
674
- ):
675
- self.mutations.append(Mutation(target, self._before))
662
+ match target:
663
+ case ast.Tuple() | ast.List():
664
+ for item in target.elts:
665
+ self._write(item, bindings)
666
+ case ast.Attribute() | ast.Subscript():
667
+ owner = self._value(target.value, bindings)
668
+ if (
669
+ owner is not None
670
+ and owner.borrowed
671
+ and (
672
+ isinstance(target, ast.Subscript)
673
+ or (
674
+ owner.shape.model is not None
675
+ and self.models.field(owner.shape.model, target.attr) is not None
676
+ )
677
+ )
678
+ ):
679
+ self.mutations.append(Mutation(target, self._before))
680
+ case _:
681
+ pass
676
682
 
677
683
  def _expression(self, node: ast.AST, bindings: dict[str, _Value]) -> None:
678
- if isinstance(node, (ast.Lambda, ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
679
- return
680
- if isinstance(node, ast.NamedExpr):
681
- self._expression(node.value, bindings)
682
- self._bind(node.target, self._value(node.value, bindings), bindings)
683
- return
684
+ match node:
685
+ case ast.Lambda() | ast.FunctionDef() | ast.AsyncFunctionDef() | ast.ClassDef():
686
+ return
687
+ case ast.NamedExpr():
688
+ self._expression(node.value, bindings)
689
+ self._bind(node.target, self._value(node.value, bindings), bindings)
690
+ return
691
+ case _:
692
+ pass
684
693
  if isinstance(node, ast.Call) and self._mutator_call(node, bindings):
685
694
  self.mutations.append(Mutation(node, self._before))
686
695
  if isinstance(node, (ast.ListComp, ast.SetComp, ast.GeneratorExp, ast.DictComp)):
@@ -305,10 +305,13 @@ def base_names(node: ast.ClassDef) -> set[str]:
305
305
  names: set[str] = set()
306
306
  for base in node.bases:
307
307
  target = base.value if isinstance(base, ast.Subscript) else base
308
- if isinstance(target, ast.Attribute):
309
- names.add(target.attr)
310
- elif isinstance(target, ast.Name):
311
- names.add(target.id)
308
+ match target:
309
+ case ast.Attribute():
310
+ names.add(target.attr)
311
+ case ast.Name():
312
+ names.add(target.id)
313
+ case _:
314
+ pass
312
315
  return names
313
316
 
314
317
 
@@ -241,10 +241,13 @@ class FastapiIndex:
241
241
  for node in walk_ast(tree):
242
242
  if self._node_scopes[id(node)] != 0:
243
243
  continue
244
- if isinstance(node, ast.Import):
245
- self._read_import(node)
246
- elif isinstance(node, ast.ImportFrom):
247
- self._read_import_from(node)
244
+ match node:
245
+ case ast.Import():
246
+ self._read_import(node)
247
+ case ast.ImportFrom():
248
+ self._read_import_from(node)
249
+ case _:
250
+ pass
248
251
 
249
252
  def _read_import(self, node: ast.Import) -> None:
250
253
  for alias in node.names:
@@ -141,10 +141,13 @@ def _module_non_import_bindings(tree: ast.Module) -> set[str]:
141
141
 
142
142
 
143
143
  def _target_names(node: ast.expr) -> set[str]:
144
- if isinstance(node, ast.Name):
145
- return {node.id}
146
- if isinstance(node, (ast.Tuple, ast.List)):
147
- return {name for element in node.elts for name in _target_names(element)}
144
+ match node:
145
+ case ast.Name():
146
+ return {node.id}
147
+ case ast.Tuple() | ast.List():
148
+ return {name for element in node.elts for name in _target_names(element)}
149
+ case _:
150
+ pass
148
151
  return set()
149
152
 
150
153
 
@@ -59,14 +59,17 @@ class RawMappingCallbacks:
59
59
  def callback(self, node: ast.expr, *, seen: frozenset[ast.AST] = frozenset()) -> _Function | None:
60
60
  if len(seen) >= _MAX_DEPTH:
61
61
  return None
62
- if isinstance(node, ast.Call):
63
- return (
64
- self._partial(node, seen)
65
- if self.qualified(node.func, node) == "functools.partial"
66
- else self._method(node, "__call__", seen)
67
- )
68
- if isinstance(node, ast.Attribute):
69
- return self._method(node.value, node.attr, seen)
62
+ match node:
63
+ case ast.Call():
64
+ return (
65
+ self._partial(node, seen)
66
+ if self.qualified(node.func, node) == "functools.partial"
67
+ else self._method(node, "__call__", seen)
68
+ )
69
+ case ast.Attribute():
70
+ return self._method(node.value, node.attr, seen)
71
+ case _:
72
+ pass
70
73
  if not isinstance(node, ast.Name) or not self._stable(node.id) or self.shadowed(node.id, node):
71
74
  return None
72
75
  binding = self.provenance.binding(node.id, node)
@@ -144,10 +147,13 @@ class RawMappingCallbacks:
144
147
  def _function(self, node: ast.AST) -> _Function | None:
145
148
  current = self.context.parents.get(node)
146
149
  while current is not None:
147
- if isinstance(current, ast.FunctionDef | ast.AsyncFunctionDef):
148
- return current
149
- if isinstance(current, ast.ClassDef | ast.Lambda):
150
- return None
150
+ match current:
151
+ case ast.FunctionDef() | ast.AsyncFunctionDef():
152
+ return current
153
+ case ast.ClassDef() | ast.Lambda():
154
+ return None
155
+ case _:
156
+ pass
151
157
  current = self.context.parents.get(current)
152
158
  return None
153
159
 
@@ -406,11 +406,14 @@ class _NamespaceFlow:
406
406
  def plain_dict(self, value: ast.expr, *, depth: int = 0) -> bool:
407
407
  if depth >= _MAX_DEPTH:
408
408
  return False
409
- if isinstance(value, ast.Name):
410
- assigned = self.assigned(value)
411
- return assigned is not None and self.plain_dict(assigned, depth=depth + 1)
412
- if isinstance(value, ast.IfExp):
413
- return self.plain_dict(value.body, depth=depth + 1) and self.plain_dict(value.orelse, depth=depth + 1)
409
+ match value:
410
+ case ast.Name():
411
+ assigned = self.assigned(value)
412
+ return assigned is not None and self.plain_dict(assigned, depth=depth + 1)
413
+ case ast.IfExp():
414
+ return self.plain_dict(value.body, depth=depth + 1) and self.plain_dict(value.orelse, depth=depth + 1)
415
+ case _:
416
+ pass
414
417
  return isinstance(value, ast.Dict) or (isinstance(value, ast.Call) and self.native.dict_call(value))
415
418
 
416
419
  def _collect(self, value: ast.expr, *, execution: ast.Call, depth: int = 0) -> None:
@@ -436,10 +439,13 @@ class _NamespaceFlow:
436
439
  def _input(self, value: ast.expr, *, depth: int = 0) -> set[ast.arg]:
437
440
  if depth >= _MAX_DEPTH:
438
441
  return set()
439
- if isinstance(value, ast.Name):
440
- return self._name_input(value, depth)
441
- if isinstance(value, ast.Dict):
442
- return self._dictionary_inputs(value, depth)
442
+ match value:
443
+ case ast.Name():
444
+ return self._name_input(value, depth)
445
+ case ast.Dict():
446
+ return self._dictionary_inputs(value, depth)
447
+ case _:
448
+ pass
443
449
  if isinstance(value, ast.Call) and self.native.dict_call(value):
444
450
  return self._constructor_inputs(value, depth)
445
451
  found: set[ast.arg] = set()
@@ -652,13 +658,16 @@ class _NamespaceEscapes:
652
658
  return None
653
659
  binding = self.flow.binding(value)
654
660
  found: set[ast.AST] = {binding} if binding is not None else set()
655
- if isinstance(value, ast.Name):
656
- assigned = self.flow.assigned(value)
657
- if isinstance(assigned, ast.Name | ast.Subscript):
658
- previous = self._carrier_roots(assigned, depth=depth + 1)
659
- return None if previous is None else found | previous
660
- elif isinstance(value, ast.Subscript):
661
- return self._carrier_roots(value.value, depth=depth + 1)
661
+ match value:
662
+ case ast.Name():
663
+ assigned = self.flow.assigned(value)
664
+ if isinstance(assigned, ast.Name | ast.Subscript):
665
+ previous = self._carrier_roots(assigned, depth=depth + 1)
666
+ return None if previous is None else found | previous
667
+ case ast.Subscript():
668
+ return self._carrier_roots(value.value, depth=depth + 1)
669
+ case _:
670
+ pass
662
671
  return found
663
672
 
664
673
 
@@ -427,10 +427,13 @@ class OpaqueContractFacts:
427
427
 
428
428
  def _enclosing_assertion(self, node: ast.AST) -> ast.Assert | None:
429
429
  while (parent := self.context.parents.get(node)) is not None:
430
- if isinstance(parent, ast.Assert):
431
- return parent
432
- if isinstance(parent, ast.FunctionDef | ast.AsyncFunctionDef | ast.Lambda):
433
- return None
430
+ match parent:
431
+ case ast.Assert():
432
+ return parent
433
+ case ast.FunctionDef() | ast.AsyncFunctionDef() | ast.Lambda():
434
+ return None
435
+ case _:
436
+ pass
434
437
  node = parent
435
438
  return None
436
439
 
@@ -534,11 +537,14 @@ class OpaqueContractFacts:
534
537
  return None
535
538
  if isinstance(node, ast.Name):
536
539
  binding = self.facts.provenance.runtime_binding(node.id, node)
537
- if isinstance(binding, ast.arg):
538
- return binding
539
- if isinstance(binding, ast.Name):
540
- value = self.assigned(binding, node)
541
- return self.parameter(value, depth=depth + 1) if value is not None else None
540
+ match binding:
541
+ case ast.arg():
542
+ return binding
543
+ case ast.Name():
544
+ value = self.assigned(binding, node)
545
+ return self.parameter(value, depth=depth + 1) if value is not None else None
546
+ case _:
547
+ pass
542
548
  receiver = _projection_receiver(node)
543
549
  parameter = self.parameter(receiver, depth=depth + 1) if receiver is not None else None
544
550
  return parameter if self._raw_annotation(parameter) else None
@@ -993,12 +999,13 @@ def _projection_receiver(node: ast.expr) -> ast.expr | None:
993
999
 
994
1000
 
995
1001
  def _control_condition(node: ast.AST, parent: ast.AST) -> tuple[ast.expr, bool] | None:
996
- if isinstance(parent, ast.IfExp):
997
- accepted, rejected = node is parent.body, node is parent.orelse
998
- elif isinstance(parent, ast.If):
999
- accepted, rejected = node in parent.body, node in parent.orelse
1000
- else:
1001
- return None
1002
+ match parent:
1003
+ case ast.IfExp():
1004
+ accepted, rejected = node is parent.body, node is parent.orelse
1005
+ case ast.If():
1006
+ accepted, rejected = node in parent.body, node in parent.orelse
1007
+ case _:
1008
+ return None
1002
1009
  return (parent.test, accepted) if accepted or rejected else None
1003
1010
 
1004
1011
 
@@ -0,0 +1,17 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import override
4
+
5
+ from sqlglot import exp, parse
6
+ from sqlglot.dialects.postgres import Postgres
7
+
8
+
9
+ def parse_postgres(source: str) -> list[exp.Expr | None]:
10
+ return parse(source, read=_PostgresForLint)
11
+
12
+
13
+ class _PostgresForLint(Postgres):
14
+ class Parser(Postgres.Parser):
15
+ @override
16
+ def _warn_unsupported(self) -> None:
17
+ """Unsupported input must not copy private SQL into dependency logs."""
@@ -1271,10 +1271,13 @@ def _module_constructor_declarations(statement: ast.stmt) -> frozenset[str]:
1271
1271
 
1272
1272
 
1273
1273
  def _direct_functions(owner: ast.stmt) -> tuple[ast.FunctionDef | ast.AsyncFunctionDef, ...]:
1274
- if isinstance(owner, ast.FunctionDef | ast.AsyncFunctionDef):
1275
- return (owner,)
1276
- if isinstance(owner, ast.ClassDef):
1277
- return tuple(item for item in owner.body if isinstance(item, ast.FunctionDef | ast.AsyncFunctionDef))
1274
+ match owner:
1275
+ case ast.FunctionDef() | ast.AsyncFunctionDef():
1276
+ return (owner,)
1277
+ case ast.ClassDef():
1278
+ return tuple(item for item in owner.body if isinstance(item, ast.FunctionDef | ast.AsyncFunctionDef))
1279
+ case _:
1280
+ pass
1278
1281
  return ()
1279
1282
 
1280
1283
 
@@ -228,10 +228,13 @@ def _decorator_terminal_names(node: ast.FunctionDef | ast.AsyncFunctionDef | ast
228
228
  names: set[str] = set()
229
229
  for decorator in node.decorator_list:
230
230
  target = decorator.func if isinstance(decorator, ast.Call) else decorator
231
- if isinstance(target, ast.Name):
232
- names.add(target.id.casefold())
233
- elif isinstance(target, ast.Attribute):
234
- names.add(target.attr.casefold())
231
+ match target:
232
+ case ast.Name():
233
+ names.add(target.id.casefold())
234
+ case ast.Attribute():
235
+ names.add(target.attr.casefold())
236
+ case _:
237
+ pass
235
238
  return frozenset(names)
236
239
 
237
240
 
@@ -228,7 +228,9 @@ from sarj_python_lint.rules.nan_hidden_by_builtin_min import NanHiddenByBuiltinM
228
228
  from sarj_python_lint.rules.no_conflicting_pydantic_alias_validation import NoConflictingPydanticAliasValidation
229
229
  from sarj_python_lint.rules.no_contradictory_type_guard import NoContradictoryTypeGuard
230
230
  from sarj_python_lint.rules.no_create_verb_in_post_path import NoCreateVerbInPostPath
231
+ from sarj_python_lint.rules.no_custom_control_flow_exception import NoCustomControlFlowException
231
232
  from sarj_python_lint.rules.no_defaulted_environment_setting import NoDefaultedEnvironmentSetting
233
+ from sarj_python_lint.rules.no_discarded_tagged_outcome import NoDiscardedTaggedOutcome
232
234
  from sarj_python_lint.rules.no_duplicate_field_validator_body import NoDuplicateFieldValidatorBody
233
235
  from sarj_python_lint.rules.no_exception_text_in_500_detail import NoExceptionTextIn500Detail
234
236
  from sarj_python_lint.rules.no_folded_bad_request_not_found import NoFoldedBadRequestNotFound
@@ -237,35 +239,57 @@ from sarj_python_lint.rules.no_impossible_pydantic_field_bounds import NoImpossi
237
239
  from sarj_python_lint.rules.no_invalid_pydantic_bypass import NoInvalidPydanticBypass
238
240
  from sarj_python_lint.rules.no_invalid_pydantic_validator_return import NoInvalidPydanticValidatorReturn
239
241
  from sarj_python_lint.rules.no_partial_type_is import NoPartialTypeIs
242
+ from sarj_python_lint.rules.no_recording_store import NoRecordingStore
240
243
  from sarj_python_lint.rules.no_redundant_local_alias import NoRedundantLocalAlias
244
+ from sarj_python_lint.rules.no_redundant_sql_alias import NoRedundantSqlAlias
241
245
  from sarj_python_lint.rules.no_shallow_record_type_guard import NoShallowRecordTypeGuard
242
246
  from sarj_python_lint.rules.no_slash_collection_root_under_prefix import NoSlashCollectionRootUnderPrefix
243
247
  from sarj_python_lint.rules.no_unknown_pydantic_validator_field import NoUnknownPydanticValidatorField
244
248
  from sarj_python_lint.rules.no_work_verb_get_route import NoWorkVerbGetRoute
249
+ from sarj_python_lint.rules.prefer_application_model_joins import PreferApplicationModelJoins
245
250
  from sarj_python_lint.rules.prefer_bulk_upsert import PreferBulkUpsert
246
251
  from sarj_python_lint.rules.prefer_direct_import_module import PreferDirectImportModule
247
252
  from sarj_python_lint.rules.prefer_discriminated_model_union import PreferDiscriminatedModelUnion
248
253
  from sarj_python_lint.rules.prefer_grouped_settings import PreferGroupedSettings
254
+ from sarj_python_lint.rules.prefer_optional_composite import PreferOptionalComposite
249
255
  from sarj_python_lint.rules.prefer_precise_object_contract import PreferPreciseObjectContract
250
256
  from sarj_python_lint.rules.prefer_psycopg_executemany import PreferPsycopgExecutemany
251
257
  from sarj_python_lint.rules.prefer_pydantic_model_assertion import PreferPydanticModelAssertion
252
258
  from sarj_python_lint.rules.prefer_pytest_dependency_fixtures import PreferPytestDependencyFixtures
259
+ from sarj_python_lint.rules.prefer_runtime_validation import PreferRuntimeValidation
260
+ from sarj_python_lint.rules.prefer_typed_cli import PreferTypedCli
261
+ from sarj_python_lint.rules.prefer_typed_mapping_get import PreferTypedMappingGet
253
262
  from sarj_python_lint.rules.prefer_typed_settings_field import PreferTypedSettingsField
254
263
  from sarj_python_lint.rules.pytest_fixture_returns_closed_resource import PytestFixtureReturnsClosedResource
255
264
  from sarj_python_lint.rules.redundant_test_constructor_forwarder import RedundantTestConstructorForwarder
256
265
  from sarj_python_lint.rules.repeated_unimplemented_test_contract import RepeatedUnimplementedTestContract
257
266
  from sarj_python_lint.rules.require_exp_on_jwt_decode import RequireExpOnJwtDecode
258
267
  from sarj_python_lint.rules.store_field_update_reuses_upsert import StoreFieldUpdateReusesUpsert
268
+ from sarj_python_lint.rules.store_insert_requires_explicit_conflict_policy import (
269
+ StoreInsertRequiresExplicitConflictPolicy,
270
+ )
271
+ from sarj_python_lint.rules.store_mutation_command_fanout import StoreMutationCommandFanout
259
272
  from sarj_python_lint.rules.test_without_explicit_oracle import NoExplicitTestOracle
260
273
 
261
274
 
262
275
  REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
263
276
  {
277
+ NoDiscardedTaggedOutcome.id: NoDiscardedTaggedOutcome,
278
+ StoreInsertRequiresExplicitConflictPolicy.id: StoreInsertRequiresExplicitConflictPolicy,
279
+ StoreMutationCommandFanout.id: StoreMutationCommandFanout,
280
+ PreferApplicationModelJoins.id: PreferApplicationModelJoins,
281
+ NoRecordingStore.id: NoRecordingStore,
282
+ NoRedundantSqlAlias.id: NoRedundantSqlAlias,
283
+ NoCustomControlFlowException.id: NoCustomControlFlowException,
284
+ PreferOptionalComposite.id: PreferOptionalComposite,
264
285
  AsyncSubprocessCancellationCleanup.id: AsyncSubprocessCancellationCleanup,
265
286
  CancelOwnedTaskWithoutJoin.id: CancelOwnedTaskWithoutJoin,
266
287
  AsyncExecutorShutdown.id: AsyncExecutorShutdown,
267
288
  NanHiddenByBuiltinMin.id: NanHiddenByBuiltinMin,
268
289
  FailedProbeZeroThreshold.id: FailedProbeZeroThreshold,
290
+ PreferTypedMappingGet.id: PreferTypedMappingGet,
291
+ PreferRuntimeValidation.id: PreferRuntimeValidation,
292
+ PreferTypedCli.id: PreferTypedCli,
269
293
  PreferGroupedSettings.id: PreferGroupedSettings,
270
294
  NoRedundantLocalAlias.id: NoRedundantLocalAlias,
271
295
  PytestFixtureReturnsClosedResource.id: PytestFixtureReturnsClosedResource,
@@ -225,12 +225,13 @@ class ResourceProvenance:
225
225
  if not isinstance(node, ast.alias):
226
226
  return False
227
227
  parent = self.parents.get(node)
228
- if isinstance(parent, ast.Import):
229
- targets.add(("import", None, 0, node.name))
230
- elif isinstance(parent, ast.ImportFrom):
231
- targets.add(("from", parent.module, parent.level, node.name))
232
- else:
233
- return False
228
+ match parent:
229
+ case ast.Import():
230
+ targets.add(("import", None, 0, node.name))
231
+ case ast.ImportFrom():
232
+ targets.add(("from", parent.module, parent.level, node.name))
233
+ case _:
234
+ return False
234
235
  return len(targets) == 1
235
236
 
236
237
  def imported(self, node: ast.expr, qualified: str) -> bool: