sarj-python-lint 1.83.1__tar.gz → 1.84.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 (291) hide show
  1. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/PKG-INFO +1 -1
  2. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/pyproject.toml +1 -1
  3. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/__main__.py +3 -2
  4. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_autofix.py +7 -4
  5. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_annotation_origins.py +43 -0
  6. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_annotation_semantics.py +7 -4
  7. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_copy_on_write.py +29 -20
  8. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_docstrings.py +7 -4
  9. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_fastapi.py +7 -4
  10. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_imports.py +7 -4
  11. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_mapping_shape_callbacks.py +18 -12
  12. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_native_object_contracts.py +25 -16
  13. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_opaque_contracts.py +22 -15
  14. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_project_index.py +7 -4
  15. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_prose_budget.py +7 -4
  16. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_registry.py +6 -0
  17. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_resource_provenance.py +7 -6
  18. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_store_writes.py +8 -5
  19. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_test_composition.py +17 -14
  20. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +8 -5
  21. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +7 -4
  22. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +14 -8
  23. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/duplicate_closed_pure_helper.py +7 -4
  24. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_broad_keyword_capture.py +7 -4
  25. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +48 -37
  26. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_invalid_pydantic_bypass.py +7 -4
  27. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +16 -10
  28. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +7 -4
  29. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +15 -9
  30. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_redundant_local_alias.py +7 -4
  31. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_repeated_string_literal.py +8 -7
  32. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +18 -8
  33. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_tautological_expect.py +29 -17
  34. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +23 -20
  35. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/over_mocked_test.py +7 -6
  36. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_class_row.py +14 -10
  37. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +7 -4
  38. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +17 -11
  39. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_declarative_batch_validation.py +14 -8
  40. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_discriminated_model_union.py +7 -4
  41. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +7 -4
  42. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +7 -4
  43. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_library_fake.py +7 -4
  44. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +151 -7
  45. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +9 -4
  46. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_native_string_check.py +14 -8
  47. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +14 -11
  48. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_or_pattern.py +7 -4
  49. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_pydantic_model_assertion.py +13 -10
  50. sarj_python_lint-1.84.0/src/sarj_python_lint/rules/prefer_runtime_validation.py +97 -0
  51. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +7 -4
  52. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +7 -4
  53. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_str_enum.py +45 -30
  54. sarj_python_lint-1.84.0/src/sarj_python_lint/rules/prefer_typed_cli.py +219 -0
  55. sarj_python_lint-1.84.0/src/sarj_python_lint/rules/prefer_typed_mapping_get.py +189 -0
  56. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +9 -6
  57. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +22 -19
  58. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/redundant_docstring.py +8 -7
  59. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +8 -5
  60. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +7 -4
  61. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +30 -21
  62. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/require_port_for_service.py +23 -16
  63. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +16 -10
  64. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/require_pydantic_for_mapping_shape.py +18 -20
  65. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +8 -5
  66. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/restated_test_docstring.py +16 -12
  67. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/stepdown.py +8 -5
  68. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +29 -17
  69. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +7 -4
  70. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +30 -24
  71. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/unused_mock_setup.py +7 -4
  72. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/.gitignore +0 -0
  73. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/LICENSE +0 -0
  74. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/README.md +0 -0
  75. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/__init__.py +0 -0
  76. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_analysis_session.py +0 -0
  77. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_autofix_aliases.py +0 -0
  78. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_autofix_annotations.py +0 -0
  79. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_autofix_comments.py +0 -0
  80. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_autofix_declared_contracts.py +0 -0
  81. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_autofix_declared_tests.py +0 -0
  82. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_autofix_docs.py +0 -0
  83. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_autofix_enums.py +0 -0
  84. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_autofix_exhaustiveness.py +0 -0
  85. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_autofix_inert.py +0 -0
  86. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_autofix_parameters.py +0 -0
  87. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_autofix_repeated.py +0 -0
  88. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_autofix_security.py +0 -0
  89. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_autofix_sql_projection.py +0 -0
  90. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_autofix_tests.py +0 -0
  91. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_file_context.py +0 -0
  92. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_filesystem.py +0 -0
  93. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_fixes.py +0 -0
  94. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_parallel.py +0 -0
  95. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_python_target.py +0 -0
  96. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_ratchet_cli.py +0 -0
  97. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_secret_names.py +0 -0
  98. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_source.py +0 -0
  99. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/_version.py +0 -0
  100. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/application_logging.py +0 -0
  101. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/autofix.py +0 -0
  102. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/benchmark.py +0 -0
  103. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/cast_access.py +0 -0
  104. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/interpreter_argv.py +0 -0
  105. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/json_boundary.py +0 -0
  106. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/py.typed +0 -0
  107. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/ratchet.py +0 -0
  108. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rule_base.py +0 -0
  109. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/__init__.py +0 -0
  110. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_ast_index.py +0 -0
  111. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_ast_names.py +0 -0
  112. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_ast_position.py +0 -0
  113. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_async_lifecycle.py +0 -0
  114. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_binding_inventory.py +0 -0
  115. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_closed_helpers.py +0 -0
  116. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_comment_narration.py +0 -0
  117. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_comments.py +0 -0
  118. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
  119. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_data_union_discriminator.py +0 -0
  120. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_declared_contracts.py +0 -0
  121. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
  122. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_exports.py +0 -0
  123. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_first_party.py +0 -0
  124. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
  125. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_inert_model_annotation.py +0 -0
  126. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_inherited_contract.py +0 -0
  127. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_local_alias_fix.py +0 -0
  128. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_local_source.py +0 -0
  129. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_logging.py +0 -0
  130. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_namespace_access.py +0 -0
  131. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_native_namespace_capability.py +0 -0
  132. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
  133. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_numeric_proof.py +0 -0
  134. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_paths.py +0 -0
  135. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_pydantic_field_contract.py +0 -0
  136. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_pytest.py +0 -0
  137. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_resolved_operation_signature.py +0 -0
  138. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_route_shapes.py +0 -0
  139. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_runtime_bindings.py +0 -0
  140. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_service_http_callbacks.py +0 -0
  141. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_set_fields.py +0 -0
  142. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_sql.py +0 -0
  143. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_sql_aggregate_identity.py +0 -0
  144. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_sql_explain.py +0 -0
  145. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_sql_policy_source.py +0 -0
  146. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_static_json_alias.py +0 -0
  147. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
  148. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
  149. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_test_provenance.py +0 -0
  150. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_transactional_sql.py +0 -0
  151. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/_type_guard_symbols.py +0 -0
  152. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
  153. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/async_executor_shutdown.py +0 -0
  154. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
  155. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/async_subprocess_cancellation_cleanup.py +0 -0
  156. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/cancel_owned_task_without_join.py +0 -0
  157. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
  158. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/delete_route_returns_no_content.py +0 -0
  159. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/duplicate_adjacent_exception_handler.py +0 -0
  160. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/duplicate_pytest_fixture_params.py +0 -0
  161. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
  162. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/failed_probe_zero_threshold.py +0 -0
  163. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
  164. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
  165. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
  166. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
  167. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
  168. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
  169. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
  170. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/nan_hidden_by_builtin_min.py +0 -0
  171. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
  172. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
  173. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
  174. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_async_function_cache.py +0 -0
  175. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_before_validator_input_mutation.py +0 -0
  176. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_blanket_suppression.py +0 -0
  177. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
  178. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_conflicting_pydantic_alias_validation.py +0 -0
  179. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
  180. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_contradictory_type_guard.py +0 -0
  181. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
  182. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
  183. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_create_verb_in_post_path.py +0 -0
  184. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
  185. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_defaulted_environment_setting.py +0 -0
  186. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
  187. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_discarded_validation_result.py +0 -0
  188. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_dual_shape_model_field_reader.py +0 -0
  189. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
  190. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
  191. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_duplicate_field_validator_body.py +0 -0
  192. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_exception_text_in_500_detail.py +0 -0
  193. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
  194. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
  195. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
  196. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
  197. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
  198. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_fixed_sleep_in_loop.py +0 -0
  199. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_folded_bad_request_not_found.py +0 -0
  200. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
  201. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
  202. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_hasattr.py +0 -0
  203. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_impossible_pydantic_field_bounds.py +0 -0
  204. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
  205. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_input_model_mutation.py +0 -0
  206. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_interpreter_source_arguments.py +0 -0
  207. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
  208. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_invalid_pydantic_validator_return.py +0 -0
  209. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_loguru_exc_info.py +0 -0
  210. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_loguru_printf_format.py +0 -0
  211. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_mocked_pydantic_value_object.py +0 -0
  212. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_mutating_container_transform.py +0 -0
  213. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
  214. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
  215. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
  216. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_partial_type_is.py +0 -0
  217. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
  218. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
  219. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
  220. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
  221. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
  222. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
  223. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
  224. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_select_star.py +0 -0
  225. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
  226. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_shallow_container_type_guard.py +0 -0
  227. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_shallow_record_type_guard.py +0 -0
  228. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_slash_collection_root_under_prefix.py +0 -0
  229. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
  230. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_test_method_grafting.py +0 -0
  231. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
  232. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
  233. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_unknown_pydantic_validator_field.py +0 -0
  234. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_unused_copy_result.py +0 -0
  235. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_unused_underscored_keyword_parameter.py +0 -0
  236. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
  237. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
  238. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
  239. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/no_work_verb_get_route.py +0 -0
  240. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
  241. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
  242. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_autospec_for_callable_mock.py +0 -0
  243. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_bulk_upsert.py +0 -0
  244. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_direct_import_module.py +0 -0
  245. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_grouped_settings.py +0 -0
  246. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
  247. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
  248. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
  249. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
  250. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
  251. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
  252. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
  253. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
  254. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_precise_object_contract.py +0 -0
  255. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_psycopg_executemany.py +0 -0
  256. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
  257. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_pytest_dependency_fixtures.py +0 -0
  258. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_pytest_fixture_injection.py +0 -0
  259. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
  260. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_required_constructor_parameters.py +0 -0
  261. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
  262. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
  263. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_typed_settings_field.py +0 -0
  264. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
  265. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
  266. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
  267. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
  268. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/pytest_fixture_returns_closed_resource.py +0 -0
  269. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
  270. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
  271. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/redundant_test_constructor_forwarder.py +0 -0
  272. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
  273. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
  274. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/repeated_unimplemented_test_contract.py +0 -0
  275. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/require_exp_on_jwt_decode.py +0 -0
  276. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
  277. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/require_injectable_retry_sleep.py +0 -0
  278. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
  279. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
  280. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/require_precise_factory_signature.py +0 -0
  281. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
  282. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
  283. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
  284. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/store_field_update_reuses_upsert.py +0 -0
  285. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
  286. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
  287. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/test_without_explicit_oracle.py +0 -0
  288. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
  289. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
  290. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.0}/src/sarj_python_lint/rules/unused_test_factory_option.py +0 -0
  291. {sarj_python_lint-1.83.1 → sarj_python_lint-1.84.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.84.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.84.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"}),
@@ -993,10 +993,13 @@ def _jwt_required_sequence(required: ast.expr) -> ast.expr | None:
993
993
  ):
994
994
  return None
995
995
  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())
996
+ match required:
997
+ case ast.List():
998
+ return ast.List(elts=elements, ctx=ast.Load())
999
+ case ast.Tuple():
1000
+ return ast.Tuple(elts=elements, ctx=ast.Load())
1001
+ case _:
1002
+ pass
1000
1003
  return ast.Set(elts=elements)
1001
1004
 
1002
1005
 
@@ -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
 
@@ -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
 
@@ -250,6 +250,9 @@ from sarj_python_lint.rules.prefer_precise_object_contract import PreferPreciseO
250
250
  from sarj_python_lint.rules.prefer_psycopg_executemany import PreferPsycopgExecutemany
251
251
  from sarj_python_lint.rules.prefer_pydantic_model_assertion import PreferPydanticModelAssertion
252
252
  from sarj_python_lint.rules.prefer_pytest_dependency_fixtures import PreferPytestDependencyFixtures
253
+ from sarj_python_lint.rules.prefer_runtime_validation import PreferRuntimeValidation
254
+ from sarj_python_lint.rules.prefer_typed_cli import PreferTypedCli
255
+ from sarj_python_lint.rules.prefer_typed_mapping_get import PreferTypedMappingGet
253
256
  from sarj_python_lint.rules.prefer_typed_settings_field import PreferTypedSettingsField
254
257
  from sarj_python_lint.rules.pytest_fixture_returns_closed_resource import PytestFixtureReturnsClosedResource
255
258
  from sarj_python_lint.rules.redundant_test_constructor_forwarder import RedundantTestConstructorForwarder
@@ -266,6 +269,9 @@ REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
266
269
  AsyncExecutorShutdown.id: AsyncExecutorShutdown,
267
270
  NanHiddenByBuiltinMin.id: NanHiddenByBuiltinMin,
268
271
  FailedProbeZeroThreshold.id: FailedProbeZeroThreshold,
272
+ PreferTypedMappingGet.id: PreferTypedMappingGet,
273
+ PreferRuntimeValidation.id: PreferRuntimeValidation,
274
+ PreferTypedCli.id: PreferTypedCli,
269
275
  PreferGroupedSettings.id: PreferGroupedSettings,
270
276
  NoRedundantLocalAlias.id: NoRedundantLocalAlias,
271
277
  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:
@@ -165,11 +165,14 @@ def _free_names(expression: ast.expr) -> Iterator[str]:
165
165
  node, shadowed = pending.pop()
166
166
  if isinstance(node, ast.Name) and isinstance(node.ctx, ast.Load) and node.id not in shadowed:
167
167
  yield node.id
168
- if isinstance(node, ast.FunctionDef | ast.AsyncFunctionDef | ast.ClassDef | ast.Lambda):
169
- continue
170
- if isinstance(node, ast.ListComp | ast.SetComp | ast.DictComp | ast.GeneratorExp):
171
- pending.extend(_comprehension_scopes(node, shadowed))
172
- continue
168
+ match node:
169
+ case ast.FunctionDef() | ast.AsyncFunctionDef() | ast.ClassDef() | ast.Lambda():
170
+ continue
171
+ case ast.ListComp() | ast.SetComp() | ast.DictComp() | ast.GeneratorExp():
172
+ pending.extend(_comprehension_scopes(node, shadowed))
173
+ continue
174
+ case _:
175
+ pass
173
176
  pending.extend((child, shadowed) for child in ast.iter_child_nodes(node))
174
177
 
175
178
 
@@ -274,20 +274,23 @@ def _value_key(
274
274
  scope: WiringScope,
275
275
  seen: frozenset[str] = frozenset(),
276
276
  ) -> str | None:
277
- if isinstance(value, ast.Name):
278
- if value.id in seen or value.id in scope.blocked:
279
- return None
280
- if value.id in scope.aliases:
281
- return _value_key(
282
- context,
283
- project,
284
- scope.aliases[value.id],
285
- scope,
286
- seen=seen | {value.id},
287
- )
288
- return f"fixture:{value.id}" if value.id in scope.parameters else None
289
- if isinstance(value, ast.Constant):
290
- return ast.dump(value)
277
+ match value:
278
+ case ast.Name():
279
+ if value.id in seen or value.id in scope.blocked:
280
+ return None
281
+ if value.id in scope.aliases:
282
+ return _value_key(
283
+ context,
284
+ project,
285
+ scope.aliases[value.id],
286
+ scope,
287
+ seen=seen | {value.id},
288
+ )
289
+ return f"fixture:{value.id}" if value.id in scope.parameters else None
290
+ case ast.Constant():
291
+ return ast.dump(value)
292
+ case _:
293
+ pass
291
294
  if not isinstance(value, ast.Call):
292
295
  return None
293
296
  callee = unalias(value.func, scope.aliases)
@@ -174,11 +174,14 @@ def _recoverable_query_root(node: ast.expr, constructors: frozenset[str]) -> ast
174
174
 
175
175
 
176
176
  def _qualified_name(node: ast.expr) -> str:
177
- if isinstance(node, ast.Name):
178
- return node.id
179
- if isinstance(node, ast.Attribute):
180
- owner = _qualified_name(node.value)
181
- return f"{owner}.{node.attr}" if owner else node.attr
177
+ match node:
178
+ case ast.Name():
179
+ return node.id
180
+ case ast.Attribute():
181
+ owner = _qualified_name(node.value)
182
+ return f"{owner}.{node.attr}" if owner else node.attr
183
+ case _:
184
+ pass
182
185
  return ""
183
186
 
184
187
 
@@ -160,10 +160,13 @@ class DocstringArgsRestateSignature(Rule):
160
160
  pending: list[tuple[ast.AST, str | None]] = [(child, class_name) for child in reversed(children(node))]
161
161
  while pending:
162
162
  child, owner = pending.pop()
163
- if isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef)):
164
- self._check_function(child, owner, path, imports, diags)
165
- elif isinstance(child, ast.ClassDef):
166
- owner = child.name
163
+ match child:
164
+ case ast.FunctionDef() | ast.AsyncFunctionDef():
165
+ self._check_function(child, owner, path, imports, diags)
166
+ case ast.ClassDef():
167
+ owner = child.name
168
+ case _:
169
+ pass
167
170
  pending.extend((nested, owner) for nested in reversed(children(child)))
168
171
 
169
172
  def _check_function(
@@ -230,10 +230,13 @@ class DocstringReturnsRestateSignature(Rule):
230
230
  pending: list[tuple[ast.AST, str | None]] = [(child, class_name) for child in reversed(children(node))]
231
231
  while pending:
232
232
  child, owner = pending.pop()
233
- if isinstance(child, ast.FunctionDef | ast.AsyncFunctionDef):
234
- self._check_function(child, owner, path, imports, diags)
235
- elif isinstance(child, ast.ClassDef):
236
- owner = child.name
233
+ match child:
234
+ case ast.FunctionDef() | ast.AsyncFunctionDef():
235
+ self._check_function(child, owner, path, imports, diags)
236
+ case ast.ClassDef():
237
+ owner = child.name
238
+ case _:
239
+ pass
237
240
  pending.extend((nested, owner) for nested in reversed(children(child)))
238
241
 
239
242
  def _check_function(
@@ -333,10 +336,13 @@ def _contains_yield(node: ast.FunctionDef | ast.AsyncFunctionDef) -> bool:
333
336
  pending: list[ast.AST] = list(node.body)
334
337
  while pending:
335
338
  current = pending.pop()
336
- if isinstance(current, (ast.Yield, ast.YieldFrom)):
337
- return True
338
- if isinstance(current, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef, ast.Lambda)):
339
- continue
339
+ match current:
340
+ case ast.Yield() | ast.YieldFrom():
341
+ return True
342
+ case ast.FunctionDef() | ast.AsyncFunctionDef() | ast.ClassDef() | ast.Lambda():
343
+ continue
344
+ case _:
345
+ pass
340
346
  pending.extend(children(current))
341
347
  return False
342
348