sarj-python-lint 1.77.0__tar.gz → 1.77.2__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 (271) hide show
  1. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/PKG-INFO +1 -1
  2. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/pyproject.toml +1 -1
  3. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_fixes.py +3 -1
  4. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_fastapi.py +136 -44
  5. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +2 -1
  6. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/.gitignore +0 -0
  7. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/LICENSE +0 -0
  8. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/README.md +0 -0
  9. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/__init__.py +0 -0
  10. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/__main__.py +0 -0
  11. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_analysis_session.py +0 -0
  12. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_autofix.py +0 -0
  13. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_autofix_aliases.py +0 -0
  14. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_autofix_annotations.py +0 -0
  15. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_autofix_comments.py +0 -0
  16. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_autofix_declared_contracts.py +0 -0
  17. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_autofix_declared_tests.py +0 -0
  18. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_autofix_docs.py +0 -0
  19. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_autofix_enums.py +0 -0
  20. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_autofix_exhaustiveness.py +0 -0
  21. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_autofix_inert.py +0 -0
  22. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_autofix_parameters.py +0 -0
  23. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_autofix_repeated.py +0 -0
  24. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_autofix_security.py +0 -0
  25. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_autofix_sql_projection.py +0 -0
  26. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_autofix_tests.py +0 -0
  27. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_file_context.py +0 -0
  28. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_filesystem.py +0 -0
  29. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_parallel.py +0 -0
  30. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_python_target.py +0 -0
  31. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_ratchet_cli.py +0 -0
  32. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_secret_names.py +0 -0
  33. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_source.py +0 -0
  34. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/_version.py +0 -0
  35. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/autofix.py +0 -0
  36. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/benchmark.py +0 -0
  37. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/cast_access.py +0 -0
  38. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/interpreter_argv.py +0 -0
  39. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/json_boundary.py +0 -0
  40. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/py.typed +0 -0
  41. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/ratchet.py +0 -0
  42. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rule_base.py +0 -0
  43. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/__init__.py +0 -0
  44. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_annotation_origins.py +0 -0
  45. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
  46. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_ast_index.py +0 -0
  47. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_ast_names.py +0 -0
  48. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_ast_position.py +0 -0
  49. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_closed_helpers.py +0 -0
  50. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_comments.py +0 -0
  51. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
  52. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_copy_on_write.py +0 -0
  53. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_data_union_discriminator.py +0 -0
  54. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_declared_contracts.py +0 -0
  55. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
  56. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_docstrings.py +0 -0
  57. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_exports.py +0 -0
  58. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_first_party.py +0 -0
  59. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
  60. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_imports.py +0 -0
  61. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_inert_model_annotation.py +0 -0
  62. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_inherited_contract.py +0 -0
  63. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_local_alias_fix.py +0 -0
  64. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_local_source.py +0 -0
  65. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_logging.py +0 -0
  66. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_mapping_shape_callbacks.py +0 -0
  67. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_namespace_access.py +0 -0
  68. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_native_namespace_capability.py +0 -0
  69. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
  70. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_paths.py +0 -0
  71. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_project_index.py +0 -0
  72. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
  73. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_pydantic_field_contract.py +0 -0
  74. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_pytest.py +0 -0
  75. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_registry.py +0 -0
  76. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_resolved_operation_signature.py +0 -0
  77. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_resource_provenance.py +0 -0
  78. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_route_shapes.py +0 -0
  79. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_runtime_bindings.py +0 -0
  80. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_set_fields.py +0 -0
  81. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_sql.py +0 -0
  82. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_static_json_alias.py +0 -0
  83. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_store_writes.py +0 -0
  84. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
  85. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
  86. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_test_composition.py +0 -0
  87. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_test_provenance.py +0 -0
  88. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/_type_guard_symbols.py +0 -0
  89. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
  90. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
  91. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
  92. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
  93. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/delete_route_returns_no_content.py +0 -0
  94. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
  95. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
  96. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/duplicate_adjacent_exception_handler.py +0 -0
  97. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/duplicate_closed_pure_helper.py +0 -0
  98. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/duplicate_pytest_fixture_params.py +0 -0
  99. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
  100. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
  101. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
  102. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
  103. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
  104. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
  105. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
  106. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
  107. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
  108. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
  109. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_async_function_cache.py +0 -0
  110. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_before_validator_input_mutation.py +0 -0
  111. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_blanket_suppression.py +0 -0
  112. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_broad_keyword_capture.py +0 -0
  113. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
  114. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_conflicting_pydantic_alias_validation.py +0 -0
  115. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
  116. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_contradictory_type_guard.py +0 -0
  117. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
  118. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
  119. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_create_verb_in_post_path.py +0 -0
  120. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
  121. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_defaulted_environment_setting.py +0 -0
  122. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
  123. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_discarded_validation_result.py +0 -0
  124. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_dual_shape_model_field_reader.py +0 -0
  125. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
  126. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
  127. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_duplicate_field_validator_body.py +0 -0
  128. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_exception_text_in_500_detail.py +0 -0
  129. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
  130. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
  131. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
  132. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
  133. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
  134. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_fixed_sleep_in_loop.py +0 -0
  135. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_folded_bad_request_not_found.py +0 -0
  136. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
  137. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
  138. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_hasattr.py +0 -0
  139. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
  140. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_impossible_pydantic_field_bounds.py +0 -0
  141. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
  142. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_input_model_mutation.py +0 -0
  143. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_interpreter_source_arguments.py +0 -0
  144. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
  145. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_invalid_pydantic_bypass.py +0 -0
  146. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_invalid_pydantic_validator_return.py +0 -0
  147. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_loguru_exc_info.py +0 -0
  148. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_loguru_printf_format.py +0 -0
  149. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_mocked_pydantic_value_object.py +0 -0
  150. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_mutating_container_transform.py +0 -0
  151. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
  152. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
  153. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
  154. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_partial_type_is.py +0 -0
  155. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
  156. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
  157. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
  158. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
  159. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
  160. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
  161. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_redundant_local_alias.py +0 -0
  162. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
  163. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
  164. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
  165. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
  166. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
  167. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_select_star.py +0 -0
  168. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
  169. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_shallow_container_type_guard.py +0 -0
  170. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_shallow_record_type_guard.py +0 -0
  171. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_slash_collection_root_under_prefix.py +0 -0
  172. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
  173. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
  174. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
  175. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_test_method_grafting.py +0 -0
  176. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
  177. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
  178. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_unknown_pydantic_validator_field.py +0 -0
  179. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
  180. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_unused_copy_result.py +0 -0
  181. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_unused_underscored_keyword_parameter.py +0 -0
  182. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
  183. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
  184. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
  185. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/no_work_verb_get_route.py +0 -0
  186. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
  187. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
  188. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
  189. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_autospec_for_callable_mock.py +0 -0
  190. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_bulk_upsert.py +0 -0
  191. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
  192. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
  193. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
  194. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_declarative_batch_validation.py +0 -0
  195. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_direct_import_module.py +0 -0
  196. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_discriminated_model_union.py +0 -0
  197. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
  198. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_grouped_settings.py +0 -0
  199. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
  200. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
  201. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
  202. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
  203. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
  204. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
  205. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
  206. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
  207. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
  208. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
  209. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
  210. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_native_string_check.py +0 -0
  211. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
  212. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
  213. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
  214. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_precise_object_contract.py +0 -0
  215. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_psycopg_executemany.py +0 -0
  216. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
  217. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_pydantic_model_assertion.py +0 -0
  218. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_pytest_dependency_fixtures.py +0 -0
  219. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_pytest_fixture_injection.py +0 -0
  220. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
  221. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_required_constructor_parameters.py +0 -0
  222. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
  223. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
  224. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
  225. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
  226. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
  227. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_typed_settings_field.py +0 -0
  228. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
  229. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
  230. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
  231. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
  232. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
  233. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
  234. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/pytest_fixture_returns_closed_resource.py +0 -0
  235. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
  236. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
  237. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
  238. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/redundant_test_constructor_forwarder.py +0 -0
  239. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
  240. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
  241. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
  242. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/repeated_unimplemented_test_contract.py +0 -0
  243. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/require_exp_on_jwt_decode.py +0 -0
  244. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
  245. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
  246. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
  247. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/require_injectable_retry_sleep.py +0 -0
  248. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
  249. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
  250. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
  251. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/require_precise_factory_signature.py +0 -0
  252. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
  253. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
  254. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/require_pydantic_for_mapping_shape.py +0 -0
  255. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
  256. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
  257. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
  258. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
  259. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/stepdown.py +0 -0
  260. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/store_field_update_reuses_upsert.py +0 -0
  261. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
  262. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
  263. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
  264. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/test_without_explicit_oracle.py +0 -0
  265. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
  266. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
  267. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
  268. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
  269. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
  270. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/src/sarj_python_lint/rules/unused_test_factory_option.py +0 -0
  271. {sarj_python_lint-1.77.0 → sarj_python_lint-1.77.2}/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.77.0
3
+ Version: 1.77.2
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.77.0"
3
+ version = "1.77.2"
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" }]
@@ -54,12 +54,14 @@ def apply_fixes(source: str, fixes: Sequence[Fix]) -> str:
54
54
  edits.sort()
55
55
  result: list[bytes] = []
56
56
  previous = 0
57
+ previous_start: int | None = None
57
58
  for start, end, replacement in edits:
58
- if start < previous:
59
+ if start < previous or start == previous_start:
59
60
  msg = "fix groups overlap"
60
61
  raise ValueError(msg)
61
62
  result.extend((raw[previous:start], replacement))
62
63
  previous = end
64
+ previous_start = start
63
65
  result.append(raw[previous:])
64
66
  fixed = b"".join(result).decode("utf-8")
65
67
  tree = ast.parse(fixed)
@@ -11,6 +11,7 @@ from sarj_python_lint.rules._imports import ImportIndex
11
11
 
12
12
 
13
13
  if TYPE_CHECKING:
14
+ from collections.abc import Sequence
14
15
  from pathlib import Path
15
16
 
16
17
 
@@ -81,6 +82,12 @@ class _ReceiverKey(NamedTuple):
81
82
  name: str
82
83
 
83
84
 
85
+ class _ScopeFrame(NamedTuple):
86
+ node: ast.AST
87
+ scope: int
88
+ class_owner: int | None
89
+
90
+
84
91
  class _DecoratorBinding(NamedTuple):
85
92
  receiver: str
86
93
  method: str
@@ -109,6 +116,24 @@ def _binding_name(node: ast.expr) -> str:
109
116
  return ""
110
117
 
111
118
 
119
+ def _binding_targets(node: ast.AST) -> Sequence[ast.expr]:
120
+ match node:
121
+ case ast.Assign(targets=targets) | ast.Delete(targets=targets):
122
+ return targets
123
+ case (
124
+ ast.AnnAssign(target=target)
125
+ | ast.AugAssign(target=target)
126
+ | ast.For(target=target)
127
+ | ast.AsyncFor(target=target)
128
+ | ast.NamedExpr(target=target)
129
+ ):
130
+ return (target,)
131
+ case ast.With(items=items) | ast.AsyncWith(items=items):
132
+ return tuple(item.optional_vars for item in items if item.optional_vars is not None)
133
+ case _:
134
+ return ()
135
+
136
+
112
137
  class FastapiIndex:
113
138
  def __init__(self, tree: ast.Module, *, path: Path | None = None) -> None:
114
139
  self.tree: ast.Module = tree
@@ -136,26 +161,81 @@ class FastapiIndex:
136
161
  self._route_scopes: dict[int, int] = {}
137
162
  self._scope_parents: dict[int, int | None] = {0: None}
138
163
  self._class_scopes: set[int] = set()
164
+ self._comprehension_scopes: set[int] = set()
139
165
  self._index_scopes(tree)
140
166
  self._read_imports(tree)
141
167
  self._read_aliases(tree)
142
168
 
143
169
  def _index_scopes(self, tree: ast.Module) -> None:
144
- pending: list[tuple[ast.AST, int, int | None]] = [(tree, 0, None)]
170
+ pending = [_ScopeFrame(tree, 0, None)]
145
171
  while pending:
146
172
  node, scope, class_owner = pending.pop()
147
173
  self._node_scopes[id(node)] = scope
148
- if isinstance(node, ast.ClassDef):
149
- class_owner = node.lineno
150
- self._class_scopes.add(node.lineno)
151
- self._scope_parents[node.lineno] = scope
152
- scope = node.lineno
153
174
  self._node_classes[id(node)] = class_owner
154
- if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
155
- self._route_scopes[id(node)] = scope
156
- self._scope_parents[node.lineno] = scope
157
- scope = node.lineno
158
- pending.extend((child, scope, class_owner) for child in reversed(tuple(ast.iter_child_nodes(node))))
175
+ match node:
176
+ case ast.ClassDef() | ast.FunctionDef() | ast.AsyncFunctionDef():
177
+ self._index_definition(node, scope, class_owner, pending)
178
+ case ast.Lambda():
179
+ self._index_lambda(node, scope, class_owner, pending)
180
+ case ast.ListComp() | ast.SetComp() | ast.DictComp() | ast.GeneratorExp():
181
+ self._index_comprehension(node, scope, class_owner, pending)
182
+ case _:
183
+ pending.extend(
184
+ _ScopeFrame(child, scope, class_owner) for child in reversed(tuple(ast.iter_child_nodes(node)))
185
+ )
186
+
187
+ def _index_definition(
188
+ self,
189
+ node: ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef,
190
+ scope: int,
191
+ class_owner: int | None,
192
+ pending: list[_ScopeFrame],
193
+ ) -> None:
194
+ body = set(node.body)
195
+ owner = class_owner
196
+ self._scope_parents[node.lineno] = scope
197
+ if isinstance(node, ast.ClassDef):
198
+ owner = node.lineno
199
+ self._class_scopes.add(node.lineno)
200
+ self._node_classes[id(node)] = owner
201
+ else:
202
+ self._route_scopes[id(node)] = scope
203
+ pending.extend(
204
+ _ScopeFrame(child, node.lineno, owner) if child in body else _ScopeFrame(child, scope, class_owner)
205
+ for child in reversed(tuple(ast.iter_child_nodes(node)))
206
+ )
207
+
208
+ def _index_lambda(self, node: ast.Lambda, scope: int, class_owner: int | None, pending: list[_ScopeFrame]) -> None:
209
+ implicit_scope = -id(node)
210
+ self._scope_parents[implicit_scope] = scope
211
+ self._bind_arguments(node.args, implicit_scope)
212
+ pending.extend(
213
+ (_ScopeFrame(node.args, scope, class_owner), _ScopeFrame(node.body, implicit_scope, class_owner))
214
+ )
215
+
216
+ def _index_comprehension(
217
+ self,
218
+ node: ast.ListComp | ast.SetComp | ast.DictComp | ast.GeneratorExp,
219
+ outer_scope: int,
220
+ class_owner: int | None,
221
+ pending: list[_ScopeFrame],
222
+ ) -> None:
223
+ scope = -id(node)
224
+ self._scope_parents[scope] = outer_scope
225
+ self._comprehension_scopes.add(scope)
226
+ for index, generator in enumerate(node.generators):
227
+ self._node_scopes[id(generator)] = scope
228
+ self._node_classes[id(generator)] = class_owner
229
+ self.bound_names.update(_ReceiverKey(scope, name) for name in self._target_names(generator.target))
230
+ pending.extend(
231
+ _ScopeFrame(child, outer_scope if index == 0 and child is generator.iter else scope, class_owner)
232
+ for child in ast.iter_child_nodes(generator)
233
+ )
234
+ pending.extend(
235
+ _ScopeFrame(child, scope, class_owner)
236
+ for child in ast.iter_child_nodes(node)
237
+ if not isinstance(child, ast.comprehension)
238
+ )
159
239
 
160
240
  def _read_imports(self, tree: ast.Module) -> None:
161
241
  for node in walk_ast(tree):
@@ -222,7 +302,17 @@ class FastapiIndex:
222
302
  changed = True
223
303
 
224
304
  def _read_bound_names(self, node: ast.AST) -> None:
225
- if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
305
+ if targets := _binding_targets(node):
306
+ self.bound_names.update(
307
+ _ReceiverKey(self._binding_scope(node), name)
308
+ for target in targets
309
+ for name in self._target_names(target)
310
+ )
311
+ elif isinstance(node, (ast.MatchAs, ast.MatchStar)) and node.name is not None:
312
+ self.bound_names.add(_ReceiverKey(self._node_scopes[id(node)], node.name))
313
+ elif isinstance(node, ast.MatchMapping) and node.rest is not None:
314
+ self.bound_names.add(_ReceiverKey(self._node_scopes[id(node)], node.rest))
315
+ elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
226
316
  self._bind_function_names(node)
227
317
  elif isinstance(node, ast.ClassDef):
228
318
  self.bound_names.add(_ReceiverKey(self._node_scopes[id(node)], node.name))
@@ -231,34 +321,30 @@ class FastapiIndex:
231
321
  _ReceiverKey(self._node_scopes[id(node)], alias.asname or alias.name.split(".")[0])
232
322
  for alias in node.names
233
323
  )
234
- elif isinstance(node, (ast.For, ast.AsyncFor)):
235
- self.bound_names.update(
236
- _ReceiverKey(self._node_scopes[id(node)], name) for name in self._target_names(node.target)
237
- )
238
- elif isinstance(node, ast.With):
239
- self._bind_with_targets(node)
240
324
  elif isinstance(node, ast.ExceptHandler) and node.name is not None:
241
325
  self.bound_names.add(_ReceiverKey(self._node_scopes[id(node)], node.name))
242
- elif isinstance(node, ast.NamedExpr):
243
- self.bound_names.update(
244
- _ReceiverKey(self._node_scopes[id(node)], name) for name in self._target_names(node.target)
245
- )
326
+
327
+ def _binding_scope(self, node: ast.AST) -> int:
328
+ scope = self._node_scopes[id(node)]
329
+ if isinstance(node, ast.NamedExpr):
330
+ while scope in self._comprehension_scopes:
331
+ parent = self._scope_parents[scope]
332
+ if parent is None:
333
+ break
334
+ scope = parent
335
+ return scope
246
336
 
247
337
  def _bind_function_names(self, node: ast.FunctionDef | ast.AsyncFunctionDef) -> None:
248
338
  self.bound_names.add(_ReceiverKey(self._node_scopes[id(node)], node.name))
249
- arguments = [*node.args.posonlyargs, *node.args.args, *node.args.kwonlyargs]
250
- if node.args.vararg is not None:
251
- arguments.append(node.args.vararg)
252
- if node.args.kwarg is not None:
253
- arguments.append(node.args.kwarg)
254
- self.bound_names.update(_ReceiverKey(node.lineno, argument.arg) for argument in arguments)
255
-
256
- def _bind_with_targets(self, node: ast.With) -> None:
257
- for item in node.items:
258
- if item.optional_vars is not None:
259
- self.bound_names.update(
260
- _ReceiverKey(self._node_scopes[id(node)], name) for name in self._target_names(item.optional_vars)
261
- )
339
+ self._bind_arguments(node.args, node.lineno)
340
+
341
+ def _bind_arguments(self, arguments: ast.arguments, scope: int) -> None:
342
+ names = [*arguments.posonlyargs, *arguments.args, *arguments.kwonlyargs]
343
+ if arguments.vararg is not None:
344
+ names.append(arguments.vararg)
345
+ if arguments.kwarg is not None:
346
+ names.append(arguments.kwarg)
347
+ self.bound_names.update(_ReceiverKey(scope, argument.arg) for argument in names)
262
348
 
263
349
  def _alias_assignment(self, node: ast.AST) -> tuple[int, str, ast.expr] | None:
264
350
  if isinstance(node, ast.Assign) and len(node.targets) == 1:
@@ -309,11 +395,15 @@ class FastapiIndex:
309
395
  self.hidden_receivers.add(key)
310
396
 
311
397
  def _target_names(self, node: ast.expr) -> tuple[str, ...]:
312
- if isinstance(node, ast.Name):
313
- return (node.id,)
314
- if isinstance(node, (ast.Tuple, ast.List)):
315
- return tuple(name for item in node.elts for name in self._target_names(item))
316
- return ()
398
+ match node:
399
+ case ast.Name(id=name):
400
+ return (name,)
401
+ case ast.Starred(value=value):
402
+ return self._target_names(value)
403
+ case ast.Tuple(elts=items) | ast.List(elts=items):
404
+ return tuple(name for item in items for name in self._target_names(item))
405
+ case _:
406
+ return ()
317
407
 
318
408
  def _assignment_scope(self, node: ast.Assign | ast.AnnAssign, name: str) -> int:
319
409
  if name.startswith("self."):
@@ -526,11 +616,13 @@ class FastapiIndex:
526
616
  if isinstance(node, ast.Attribute):
527
617
  root = self._root_name(node)
528
618
  node_scope = self._node_scopes.get(id(node), 0)
529
- # An annotation is evaluated outside the function whose signature owns
530
- # it, so begin with that function's parent scope.
531
- scope: int | None = self._scope_parents.get(node_scope)
619
+ # The index records where the annotation is evaluated. A method body
620
+ # does not close over an intervening class namespace.
621
+ scope: int | None = node_scope
532
622
  while scope is not None and scope != 0:
533
- if _ReceiverKey(scope, root) in self.bound_names:
623
+ if (scope == node_scope or scope not in self._class_scopes) and _ReceiverKey(
624
+ scope, root
625
+ ) in self.bound_names:
534
626
  return True
535
627
  scope = self._scope_parents.get(scope)
536
628
  return False
@@ -927,11 +927,12 @@ def _check_operation_ids(routes: list[tuple[int, Route]], parents: Mapping[ast.A
927
927
  seen: dict[tuple[int, str, str], Route] = {}
928
928
  reported: set[int] = set()
929
929
  for scope, route in sorted(routes, key=lambda item: item[1].decorator.lineno):
930
+ schema_override = route.has_unpack or "openapi_extra" in route.keywords
930
931
  if (
931
932
  route.is_hidden
932
933
  or route.path is None
933
934
  or route.method == "*"
934
- or "openapi_extra" in route.keywords
935
+ or schema_override
935
936
  or _conditional_registration(route.decorator, parents)
936
937
  ):
937
938
  continue