sarj-python-lint 1.83.0__tar.gz → 1.83.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/PKG-INFO +1 -1
  2. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/pyproject.toml +1 -1
  3. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_enums.py +1 -1
  4. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_repeated.py +1 -1
  5. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_file_context.py +11 -1
  6. sarj_python_lint-1.83.1/src/sarj_python_lint/rules/_binding_inventory.py +131 -0
  7. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_closed_helpers.py +1 -1
  8. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_data_union_discriminator.py +3 -1
  9. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_project_index.py +65 -56
  10. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_resource_provenance.py +13 -81
  11. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_interpreter_source_arguments.py +39 -22
  12. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_redundant_local_alias.py +1 -1
  13. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_required_constructor_parameters.py +10 -8
  14. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/unused_mock_setup.py +12 -1
  15. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/.gitignore +0 -0
  16. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/LICENSE +0 -0
  17. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/README.md +0 -0
  18. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/__init__.py +0 -0
  19. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/__main__.py +0 -0
  20. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_analysis_session.py +0 -0
  21. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix.py +0 -0
  22. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_aliases.py +0 -0
  23. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_annotations.py +0 -0
  24. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_comments.py +0 -0
  25. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_declared_contracts.py +0 -0
  26. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_declared_tests.py +0 -0
  27. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_docs.py +0 -0
  28. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_exhaustiveness.py +0 -0
  29. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_inert.py +0 -0
  30. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_parameters.py +0 -0
  31. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_security.py +0 -0
  32. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_sql_projection.py +0 -0
  33. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_tests.py +0 -0
  34. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_filesystem.py +0 -0
  35. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_fixes.py +0 -0
  36. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_parallel.py +0 -0
  37. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_python_target.py +0 -0
  38. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_ratchet_cli.py +0 -0
  39. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_secret_names.py +0 -0
  40. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_source.py +0 -0
  41. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_version.py +0 -0
  42. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/application_logging.py +0 -0
  43. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/autofix.py +0 -0
  44. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/benchmark.py +0 -0
  45. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/cast_access.py +0 -0
  46. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/interpreter_argv.py +0 -0
  47. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/json_boundary.py +0 -0
  48. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/py.typed +0 -0
  49. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/ratchet.py +0 -0
  50. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rule_base.py +0 -0
  51. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/__init__.py +0 -0
  52. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_annotation_origins.py +0 -0
  53. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
  54. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_ast_index.py +0 -0
  55. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_ast_names.py +0 -0
  56. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_ast_position.py +0 -0
  57. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_async_lifecycle.py +0 -0
  58. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_comment_narration.py +0 -0
  59. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_comments.py +0 -0
  60. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
  61. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_copy_on_write.py +0 -0
  62. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_declared_contracts.py +0 -0
  63. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
  64. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_docstrings.py +0 -0
  65. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_exports.py +0 -0
  66. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_fastapi.py +0 -0
  67. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_first_party.py +0 -0
  68. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
  69. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_imports.py +0 -0
  70. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_inert_model_annotation.py +0 -0
  71. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_inherited_contract.py +0 -0
  72. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_local_alias_fix.py +0 -0
  73. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_local_source.py +0 -0
  74. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_logging.py +0 -0
  75. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_mapping_shape_callbacks.py +0 -0
  76. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_namespace_access.py +0 -0
  77. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_native_namespace_capability.py +0 -0
  78. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_native_object_contracts.py +0 -0
  79. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
  80. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_numeric_proof.py +0 -0
  81. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_opaque_contracts.py +0 -0
  82. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_paths.py +0 -0
  83. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
  84. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_pydantic_field_contract.py +0 -0
  85. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_pytest.py +0 -0
  86. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_registry.py +0 -0
  87. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_resolved_operation_signature.py +0 -0
  88. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_route_shapes.py +0 -0
  89. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_runtime_bindings.py +0 -0
  90. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_service_http_callbacks.py +0 -0
  91. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_set_fields.py +0 -0
  92. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_sql.py +0 -0
  93. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_sql_aggregate_identity.py +0 -0
  94. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_sql_explain.py +0 -0
  95. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_sql_policy_source.py +0 -0
  96. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_static_json_alias.py +0 -0
  97. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_store_writes.py +0 -0
  98. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
  99. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
  100. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_test_composition.py +0 -0
  101. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_test_provenance.py +0 -0
  102. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_transactional_sql.py +0 -0
  103. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_type_guard_symbols.py +0 -0
  104. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
  105. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/async_executor_shutdown.py +0 -0
  106. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
  107. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/async_subprocess_cancellation_cleanup.py +0 -0
  108. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/cancel_owned_task_without_join.py +0 -0
  109. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
  110. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
  111. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/delete_route_returns_no_content.py +0 -0
  112. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
  113. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
  114. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/duplicate_adjacent_exception_handler.py +0 -0
  115. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/duplicate_closed_pure_helper.py +0 -0
  116. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/duplicate_pytest_fixture_params.py +0 -0
  117. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
  118. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/failed_probe_zero_threshold.py +0 -0
  119. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
  120. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
  121. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
  122. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
  123. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
  124. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
  125. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
  126. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/nan_hidden_by_builtin_min.py +0 -0
  127. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
  128. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
  129. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
  130. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_async_function_cache.py +0 -0
  131. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_before_validator_input_mutation.py +0 -0
  132. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_blanket_suppression.py +0 -0
  133. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_broad_keyword_capture.py +0 -0
  134. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
  135. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_conflicting_pydantic_alias_validation.py +0 -0
  136. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
  137. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_contradictory_type_guard.py +0 -0
  138. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
  139. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
  140. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_create_verb_in_post_path.py +0 -0
  141. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
  142. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_defaulted_environment_setting.py +0 -0
  143. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
  144. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_discarded_validation_result.py +0 -0
  145. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_dual_shape_model_field_reader.py +0 -0
  146. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
  147. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
  148. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_duplicate_field_validator_body.py +0 -0
  149. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_exception_text_in_500_detail.py +0 -0
  150. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
  151. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
  152. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
  153. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
  154. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
  155. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_fixed_sleep_in_loop.py +0 -0
  156. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_folded_bad_request_not_found.py +0 -0
  157. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
  158. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
  159. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_hasattr.py +0 -0
  160. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
  161. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_impossible_pydantic_field_bounds.py +0 -0
  162. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
  163. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_input_model_mutation.py +0 -0
  164. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
  165. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_invalid_pydantic_bypass.py +0 -0
  166. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_invalid_pydantic_validator_return.py +0 -0
  167. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_loguru_exc_info.py +0 -0
  168. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_loguru_printf_format.py +0 -0
  169. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_mocked_pydantic_value_object.py +0 -0
  170. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_mutating_container_transform.py +0 -0
  171. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
  172. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
  173. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
  174. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_partial_type_is.py +0 -0
  175. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
  176. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
  177. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
  178. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
  179. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
  180. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
  181. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
  182. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
  183. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
  184. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
  185. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
  186. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_select_star.py +0 -0
  187. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
  188. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_shallow_container_type_guard.py +0 -0
  189. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_shallow_record_type_guard.py +0 -0
  190. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_slash_collection_root_under_prefix.py +0 -0
  191. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
  192. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
  193. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
  194. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_test_method_grafting.py +0 -0
  195. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
  196. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
  197. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_unknown_pydantic_validator_field.py +0 -0
  198. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
  199. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_unused_copy_result.py +0 -0
  200. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_unused_underscored_keyword_parameter.py +0 -0
  201. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
  202. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
  203. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
  204. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_work_verb_get_route.py +0 -0
  205. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
  206. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
  207. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
  208. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_autospec_for_callable_mock.py +0 -0
  209. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_bulk_upsert.py +0 -0
  210. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
  211. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
  212. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
  213. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_declarative_batch_validation.py +0 -0
  214. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_direct_import_module.py +0 -0
  215. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_discriminated_model_union.py +0 -0
  216. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
  217. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_grouped_settings.py +0 -0
  218. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
  219. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
  220. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
  221. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
  222. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
  223. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
  224. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
  225. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
  226. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
  227. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
  228. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
  229. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_native_string_check.py +0 -0
  230. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
  231. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
  232. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
  233. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_precise_object_contract.py +0 -0
  234. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_psycopg_executemany.py +0 -0
  235. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
  236. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_pydantic_model_assertion.py +0 -0
  237. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_pytest_dependency_fixtures.py +0 -0
  238. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_pytest_fixture_injection.py +0 -0
  239. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
  240. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
  241. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
  242. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
  243. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
  244. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
  245. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_typed_settings_field.py +0 -0
  246. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
  247. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
  248. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
  249. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
  250. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
  251. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
  252. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/pytest_fixture_returns_closed_resource.py +0 -0
  253. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
  254. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
  255. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
  256. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/redundant_test_constructor_forwarder.py +0 -0
  257. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
  258. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
  259. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
  260. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/repeated_unimplemented_test_contract.py +0 -0
  261. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/require_exp_on_jwt_decode.py +0 -0
  262. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
  263. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
  264. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
  265. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/require_injectable_retry_sleep.py +0 -0
  266. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
  267. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
  268. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
  269. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/require_precise_factory_signature.py +0 -0
  270. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
  271. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
  272. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/require_pydantic_for_mapping_shape.py +0 -0
  273. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
  274. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
  275. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
  276. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
  277. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/stepdown.py +0 -0
  278. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/store_field_update_reuses_upsert.py +0 -0
  279. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
  280. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
  281. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
  282. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/test_without_explicit_oracle.py +0 -0
  283. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
  284. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
  285. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
  286. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
  287. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/unused_test_factory_option.py +0 -0
  288. {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/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.0
3
+ Version: 1.83.1
4
4
  Summary: Custom Python lint rules — AST-based, pre-commit-friendly, hypermodern defaults
5
5
  Project-URL: Homepage, https://code-standards.sarj.ai/rules/python/
6
6
  Project-URL: Documentation, https://code-standards.sarj.ai/rules/python/
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sarj-python-lint"
3
- version = "1.83.0"
3
+ version = "1.83.1"
4
4
  description = "Custom Python lint rules — AST-based, pre-commit-friendly, hypermodern defaults"
5
5
  readme = "README.md"
6
6
  authors = [{ name = "sarj-ai" }]
@@ -164,7 +164,7 @@ class EnumFixes:
164
164
 
165
165
  def _stable_alias(self, alias: ast.TypeAlias) -> bool:
166
166
  if any(
167
- bindings.get(alias.name.id, []) not in ([], [alias.name]) for bindings in self.provenance.bindings.values()
167
+ bindings.get(alias.name.id, ()) not in {(), (alias.name,)} for bindings in self.provenance.bindings.values()
168
168
  ):
169
169
  return False
170
170
  if any(
@@ -115,7 +115,7 @@ class RepeatedLiteralFixes:
115
115
 
116
116
  def _stable_binding(self, binding: ast.Name, literal: ast.Constant, parent: ast.FunctionDef) -> bool:
117
117
  return (
118
- self.provenance.bindings.get(parent, {}).get(binding.id) == [binding]
118
+ self.provenance.bindings.get(parent, {}).get(binding.id) == (binding,)
119
119
  and self.provenance.binding(binding.id, literal) is binding
120
120
  and binding.id not in self.provenance.external_bindings
121
121
  and (binding.lineno, binding.col_offset + 1) not in self.locations
@@ -8,10 +8,12 @@ from sarj_python_lint._analysis_session import AnalysisSession
8
8
  from sarj_python_lint._source import python_source_lines, symbol_prefilter_source
9
9
  from sarj_python_lint.rules._annotation_semantics import AnnotationSemantics
10
10
  from sarj_python_lint.rules._ast_index import NodeIndex
11
+ from sarj_python_lint.rules._binding_inventory import BindingInventory
11
12
  from sarj_python_lint.rules._fastapi import FastapiIndex
12
13
  from sarj_python_lint.rules._imports import ImportIndex
13
14
  from sarj_python_lint.rules._namespace_access import dynamic_namespace_access
14
15
  from sarj_python_lint.rules._paths import is_generated
16
+ from sarj_python_lint.rules._runtime_bindings import RuntimeBindings
15
17
 
16
18
 
17
19
  if TYPE_CHECKING:
@@ -23,7 +25,7 @@ if TYPE_CHECKING:
23
25
  from sarj_python_lint.rules._test_provenance import TestProvenance
24
26
 
25
27
 
26
- @final
28
+ @final # ruff: ignore[too-many-public-methods] -- one file owns the lazy fact caches and their shared lifetime; splitting providers would duplicate semantic state.
27
29
  class PythonFileContext:
28
30
  def __init__(self, path: Path, source: str, session: AnalysisSession | None = None) -> None:
29
31
  self.path = path
@@ -85,6 +87,14 @@ class PythonFileContext:
85
87
  def parents(self) -> Mapping[ast.AST, ast.AST]:
86
88
  return self.node_index.parents
87
89
 
90
+ @cached_property
91
+ def binding_inventory(self) -> BindingInventory:
92
+ return BindingInventory.from_context(self)
93
+
94
+ @cached_property
95
+ def runtime_bindings(self) -> RuntimeBindings:
96
+ return RuntimeBindings(self, self.binding_inventory.bindings)
97
+
88
98
  @cached_property
89
99
  def imports(self) -> ImportIndex:
90
100
  return ImportIndex.from_tree(self._valid_tree())
@@ -0,0 +1,131 @@
1
+ from __future__ import annotations
2
+
3
+ import ast
4
+ from collections import defaultdict
5
+ from collections.abc import Mapping
6
+ from dataclasses import dataclass
7
+ from types import MappingProxyType
8
+ from typing import TYPE_CHECKING, final
9
+
10
+
11
+ if TYPE_CHECKING:
12
+ from sarj_python_lint._file_context import PythonFileContext
13
+
14
+
15
+ _SCOPES = (
16
+ ast.Module,
17
+ ast.FunctionDef,
18
+ ast.AsyncFunctionDef,
19
+ ast.ClassDef,
20
+ ast.Lambda,
21
+ ast.ListComp,
22
+ ast.SetComp,
23
+ ast.DictComp,
24
+ ast.GeneratorExp,
25
+ )
26
+
27
+
28
+ @final
29
+ @dataclass(frozen=True, slots=True)
30
+ class BindingInventory:
31
+ parents: Mapping[ast.AST, ast.AST]
32
+ bindings: Mapping[ast.AST | None, Mapping[str, tuple[ast.AST, ...]]]
33
+ reads: Mapping[str, tuple[ast.Name, ...]]
34
+ wildcard: bool
35
+ mutated: frozenset[str]
36
+ external_bindings: frozenset[str]
37
+
38
+ @classmethod
39
+ def from_context(cls, context: PythonFileContext) -> BindingInventory:
40
+ parents = context.parents
41
+ bindings: dict[ast.AST | None, dict[str, list[ast.AST]]] = defaultdict(lambda: defaultdict(list))
42
+ reads: dict[str, list[ast.Name]] = defaultdict(list)
43
+ mutated: set[str] = set()
44
+ external_bindings: set[str] = set()
45
+ wildcard = False
46
+ for node in context.nodes(ast.AST):
47
+ if isinstance(node, ast.Name) and isinstance(node.ctx, ast.Load):
48
+ reads[node.id].append(node)
49
+ for name in _bound_names(node):
50
+ bindings[_binding_scope(parents, node)][name].append(node)
51
+ wildcard |= name == "*"
52
+ if isinstance(node, ast.Global | ast.Nonlocal):
53
+ external_bindings.update(node.names)
54
+ if (name := _mutated_namespace(node)) is not None:
55
+ mutated.add(name)
56
+ return cls(
57
+ parents=parents,
58
+ bindings=MappingProxyType(
59
+ {
60
+ scope: MappingProxyType({name: tuple(nodes) for name, nodes in names.items()})
61
+ for scope, names in bindings.items()
62
+ }
63
+ ),
64
+ reads=MappingProxyType({name: tuple(nodes) for name, nodes in reads.items()}),
65
+ wildcard=wildcard,
66
+ mutated=frozenset(mutated),
67
+ external_bindings=frozenset(external_bindings),
68
+ )
69
+
70
+ def scope(self, node: ast.AST) -> ast.AST | None:
71
+ return _scope(self.parents, node)
72
+
73
+
74
+ def _mutated_namespace(node: ast.AST) -> str | None:
75
+ if not isinstance(node, ast.Attribute) or not isinstance(node.ctx, ast.Store | ast.Del):
76
+ return None
77
+ root = node.value
78
+ while isinstance(root, ast.Attribute):
79
+ root = root.value
80
+ return root.id if isinstance(root, ast.Name) else None
81
+
82
+
83
+ def _scope(parents: Mapping[ast.AST, ast.AST], node: ast.AST) -> ast.AST | None:
84
+ parent = parents.get(node)
85
+ while parent is not None:
86
+ if isinstance(parent, ast.arguments) and (node in parent.defaults or node in parent.kw_defaults):
87
+ parent = parents.get(parent)
88
+ elif isinstance(parent, _SCOPES) and not (
89
+ (
90
+ isinstance(parent, ast.FunctionDef | ast.AsyncFunctionDef | ast.ClassDef)
91
+ and node in parent.decorator_list
92
+ )
93
+ or (isinstance(parent, ast.ClassDef) and (node in parent.bases or node in parent.keywords))
94
+ ):
95
+ return parent
96
+ if parent is None:
97
+ return None
98
+ node = parent
99
+ parent = parents.get(parent)
100
+ return None
101
+
102
+
103
+ def _binding_scope(parents: Mapping[ast.AST, ast.AST], node: ast.AST) -> ast.AST | None:
104
+ scope = _scope(parents, node)
105
+ parent = parents.get(node)
106
+ if isinstance(parent, ast.NamedExpr) and parent.target is node:
107
+ while isinstance(scope, ast.ListComp | ast.SetComp | ast.DictComp | ast.GeneratorExp):
108
+ scope = _scope(parents, scope)
109
+ return scope
110
+
111
+
112
+ def _bound_names(node: ast.AST) -> tuple[str, ...]:
113
+ match node:
114
+ case ast.Name(id=name, ctx=ast.Store() | ast.Del()) | ast.arg(arg=name):
115
+ return (name,)
116
+ case ast.alias():
117
+ return (node.asname or node.name.partition(".")[0],)
118
+ case (
119
+ ast.FunctionDef(name=name)
120
+ | ast.AsyncFunctionDef(name=name)
121
+ | ast.ClassDef(name=name)
122
+ | ast.ExceptHandler(name=str() as name)
123
+ | ast.MatchAs(name=str() as name)
124
+ | ast.MatchStar(name=str() as name)
125
+ | ast.MatchMapping(rest=str() as name)
126
+ ):
127
+ return (name,)
128
+ case ast.Global(names=names) | ast.Nonlocal(names=names):
129
+ return tuple(names)
130
+ case _:
131
+ return ()
@@ -115,7 +115,7 @@ def call_matches(function: ast.FunctionDef, call: ast.Call) -> bool:
115
115
  def stable_class(context: PythonFileContext, declaration: ast.ClassDef, provenance: ResourceProvenance) -> bool:
116
116
  if declaration.decorator_list or declaration.keywords or context.parents.get(declaration) is not context.tree:
117
117
  return False
118
- return provenance.bindings.get(context.tree, {}).get(declaration.name) == [declaration]
118
+ return provenance.bindings.get(context.tree, {}).get(declaration.name) == (declaration,)
119
119
 
120
120
 
121
121
  def plain_class_members(
@@ -11,6 +11,8 @@ from sarj_python_lint.rules._resource_provenance import ResourceProvenance
11
11
 
12
12
 
13
13
  if TYPE_CHECKING:
14
+ from collections.abc import Mapping, Sequence
15
+
14
16
  from sarj_python_lint._file_context import PythonFileContext
15
17
 
16
18
 
@@ -757,7 +759,7 @@ def _metadata_tuple_index(node: ast.expr) -> int | None:
757
759
  return None
758
760
 
759
761
 
760
- def _metadata_alias_graph(tree: ast.Module, bindings: dict[str, list[ast.AST]]) -> dict[str, set[str]]:
762
+ def _metadata_alias_graph(tree: ast.Module, bindings: Mapping[str, Sequence[ast.AST]]) -> dict[str, set[str]]:
761
763
  graph: dict[str, set[str]] = {}
762
764
  for node in tree.body:
763
765
  binding = _value_binding(node)
@@ -141,6 +141,7 @@ class ClassSummary:
141
141
 
142
142
  @dataclass(frozen=True, slots=True)
143
143
  class SourceUnit:
144
+ # Indexes borrow tree and imports; callers must keep them unchanged for each index's lifetime.
144
145
  path: Path
145
146
  module: str | None
146
147
  source: str
@@ -194,6 +195,10 @@ def imports_outside(tree: ast.Module | None, modules: frozenset[str]) -> bool:
194
195
 
195
196
  @final
196
197
  class ProjectIndexSet:
198
+ # Borrowed ASTs and import mappings must stay unchanged for this index's lifetime.
199
+ # They remain mutable Python objects; in-place mutation can leave cached facts stale.
200
+ # Use with_unit() with a fresh unit, or rebuild from an updated source snapshot.
201
+ # Snapshots capture source strings, not later AST or import mutations.
197
202
  def __init__(self, units: Mapping[Path, SourceUnit], roots: Sequence[Path] = ()) -> None:
198
203
  self._units = MappingProxyType(dict(units))
199
204
  self._roots = tuple(roots)
@@ -550,13 +555,17 @@ class ProjectIndexSet:
550
555
  def constructor_consumers(self, unit: SourceUnit, name: str) -> frozenset[tuple[Path, str]]:
551
556
  if unit.module is None:
552
557
  return frozenset()
553
- target = SymbolRef(unit.module, name)
554
- consumers: set[tuple[Path, str]] = set()
558
+ return self._constructor_consumers.get(SymbolRef(unit.module, name), frozenset())
559
+
560
+ @cached_property
561
+ def _constructor_consumers(self) -> Mapping[SymbolRef, frozenset[tuple[Path, str]]]:
562
+ consumers: dict[SymbolRef, set[tuple[Path, str]]] = {}
555
563
  for candidate in self._units.values():
556
564
  if candidate.tree is None:
557
565
  continue
558
- consumers.update(_unit_constructor_consumers(candidate, candidate.tree, target))
559
- return frozenset(consumers)
566
+ for symbol, owner in _unit_constructor_consumers(candidate, candidate.tree):
567
+ consumers.setdefault(symbol, set()).add((candidate.path, owner))
568
+ return MappingProxyType({symbol: frozenset(owners) for symbol, owners in consumers.items()})
560
569
 
561
570
  def constructor_operations(self, unit: SourceUnit, name: str) -> frozenset[tuple[Path, str]]:
562
571
  if unit.module is None:
@@ -582,27 +591,38 @@ class ProjectIndexSet:
582
591
  def direct_subclasses(self, unit: SourceUnit, name: str) -> frozenset[tuple[Path, str]]:
583
592
  if unit.module is None:
584
593
  return frozenset()
585
- target = SymbolRef(unit.module, name)
586
- return frozenset(
587
- (candidate.path, node.name)
588
- for candidate in self._units.values()
589
- if candidate.tree is not None
590
- for node in candidate.tree.body
591
- if isinstance(node, ast.ClassDef)
592
- and any(_direct_base_symbol(candidate, base) == target for base in node.bases)
593
- )
594
+ return self._direct_subclasses.get(SymbolRef(unit.module, name), frozenset())
595
+
596
+ @cached_property
597
+ def _direct_subclasses(self) -> Mapping[SymbolRef, frozenset[tuple[Path, str]]]:
598
+ subclasses: dict[SymbolRef, set[tuple[Path, str]]] = {}
599
+ for candidate in self._units.values():
600
+ if candidate.tree is None:
601
+ continue
602
+ for node in candidate.tree.body:
603
+ if not isinstance(node, ast.ClassDef):
604
+ continue
605
+ for base in node.bases:
606
+ if (symbol := _direct_base_symbol(candidate, base)) is not None:
607
+ subclasses.setdefault(symbol, set()).add((candidate.path, node.name))
608
+ return MappingProxyType({symbol: frozenset(owners) for symbol, owners in subclasses.items()})
594
609
 
595
610
  def typed_class_consumers(self, unit: SourceUnit, name: str) -> frozenset[tuple[Path, str]]:
596
611
  if unit.module is None:
597
612
  return frozenset()
598
- target = SymbolRef(unit.module, name)
599
- return frozenset(
600
- (candidate.path, owner.name)
601
- for candidate in self._units.values()
602
- if candidate.tree is not None
603
- for owner in candidate.tree.body
604
- if isinstance(owner, ast.ClassDef) and _class_has_typed_dependency(candidate, owner, target)
605
- )
613
+ return self._typed_class_consumers.get(SymbolRef(unit.module, name), frozenset())
614
+
615
+ @cached_property
616
+ def _typed_class_consumers(self) -> Mapping[SymbolRef, frozenset[tuple[Path, str]]]:
617
+ consumers: dict[SymbolRef, set[tuple[Path, str]]] = {}
618
+ for candidate in self._units.values():
619
+ if candidate.tree is None:
620
+ continue
621
+ for owner in candidate.tree.body:
622
+ if isinstance(owner, ast.ClassDef):
623
+ for symbol in _class_typed_dependencies(candidate, owner):
624
+ consumers.setdefault(symbol, set()).add((candidate.path, owner.name))
625
+ return MappingProxyType({symbol: frozenset(owners) for symbol, owners in consumers.items()})
606
626
 
607
627
  @cached_property
608
628
  def _mock_specs(self) -> Mapping[SymbolRef, frozenset[Path]]:
@@ -1195,29 +1215,23 @@ def _python_files(root: Path) -> Iterator[Path]:
1195
1215
  yield parent / name
1196
1216
 
1197
1217
 
1198
- def _unit_constructor_consumers(
1199
- candidate: SourceUnit, tree: ast.Module, target: SymbolRef
1200
- ) -> Iterator[tuple[Path, str]]:
1218
+ def _unit_constructor_consumers(candidate: SourceUnit, tree: ast.Module) -> Iterator[tuple[SymbolRef, str]]:
1201
1219
  for owner in tree.body:
1202
1220
  if not isinstance(owner, ast.ClassDef):
1203
1221
  continue
1204
- initializer = next(
1205
- (
1206
- statement
1207
- for statement in owner.body
1208
- if isinstance(statement, (ast.FunctionDef, ast.AsyncFunctionDef)) and statement.name == "__init__"
1209
- ),
1210
- None,
1211
- )
1212
- if initializer is None:
1222
+ for symbol in _constructor_annotation_symbols(candidate, owner):
1223
+ yield symbol, owner.name
1224
+
1225
+
1226
+ def _constructor_annotation_symbols(candidate: SourceUnit, owner: ast.ClassDef) -> Iterator[SymbolRef]:
1227
+ for initializer in _direct_functions(owner):
1228
+ if initializer.name != "__init__":
1213
1229
  continue
1214
1230
  parameters = (*initializer.args.posonlyargs, *initializer.args.args, *initializer.args.kwonlyargs)
1215
- if any(
1216
- _annotation_contains_symbol(candidate, parameter.annotation, target)
1217
- for parameter in parameters
1218
- if parameter.arg != "self"
1219
- ):
1220
- yield (candidate.path, owner.name)
1231
+ for parameter in parameters:
1232
+ if parameter.arg != "self":
1233
+ yield from _annotation_symbols(candidate, parameter.annotation)
1234
+ return
1221
1235
 
1222
1236
 
1223
1237
  def _unit_constructor_operations(
@@ -1374,19 +1388,16 @@ def _bound_constructor_names(node: ast.AST) -> frozenset[str]:
1374
1388
  return frozenset(names)
1375
1389
 
1376
1390
 
1377
- def _class_has_typed_dependency(unit: SourceUnit, owner: ast.ClassDef, target: SymbolRef) -> bool:
1391
+ def _class_typed_dependencies(unit: SourceUnit, owner: ast.ClassDef) -> Iterator[SymbolRef]:
1378
1392
  for statement in owner.body:
1379
- if isinstance(statement, ast.AnnAssign) and _annotation_contains_symbol(unit, statement.annotation, target):
1380
- return True
1393
+ if isinstance(statement, ast.AnnAssign):
1394
+ yield from _annotation_symbols(unit, statement.annotation)
1381
1395
  if not isinstance(statement, (ast.FunctionDef, ast.AsyncFunctionDef)):
1382
1396
  continue
1383
1397
  parameters = (*statement.args.posonlyargs, *statement.args.args, *statement.args.kwonlyargs)
1384
- if any(
1385
- parameter.arg not in {"self", "cls"} and _annotation_contains_symbol(unit, parameter.annotation, target)
1386
- for parameter in parameters
1387
- ):
1388
- return True
1389
- return False
1398
+ for parameter in parameters:
1399
+ if parameter.arg not in {"self", "cls"}:
1400
+ yield from _annotation_symbols(unit, parameter.annotation)
1390
1401
 
1391
1402
 
1392
1403
  def _mock_spec_targets(unit: SourceUnit, call: ast.Call) -> Iterator[SymbolRef]:
@@ -1444,17 +1455,15 @@ def _absolute_import_symbol(statement: ast.stmt, name: str) -> SymbolRef | None:
1444
1455
  return None
1445
1456
 
1446
1457
 
1447
- def _annotation_contains_symbol(unit: SourceUnit, annotation: ast.expr | None, target: SymbolRef) -> bool:
1458
+ def _annotation_symbols(unit: SourceUnit, annotation: ast.expr | None) -> Iterator[SymbolRef]:
1448
1459
  if annotation is None:
1449
- return False
1460
+ return
1450
1461
  if isinstance(annotation, ast.Constant) and isinstance(annotation.value, str):
1451
- with suppress(SyntaxError):
1462
+ with suppress(SyntaxError, UnicodeError, RecursionError):
1452
1463
  annotation = ast.parse(annotation.value, mode="eval").body
1453
- return any(
1454
- _resolve(unit, candidate) == target
1455
- for candidate in walk_ast(annotation)
1456
- if isinstance(candidate, (ast.Name, ast.Attribute))
1457
- )
1464
+ for candidate in walk_ast(annotation):
1465
+ if isinstance(candidate, (ast.Name, ast.Attribute)) and (symbol := _resolve(unit, candidate)) is not None:
1466
+ yield symbol
1458
1467
 
1459
1468
 
1460
1469
  def _record_import(node: ast.stmt, package: str, result: dict[str, SymbolRef]) -> None:
@@ -8,27 +8,16 @@ from typing import TYPE_CHECKING, final
8
8
  from weakref import ReferenceType, ref
9
9
 
10
10
  from sarj_python_lint.rules._ast_index import has_own_yield
11
- from sarj_python_lint.rules._runtime_bindings import RuntimeBindings
12
11
 
13
12
 
14
13
  if TYPE_CHECKING:
15
- from collections.abc import Callable
14
+ from collections.abc import Callable, Sequence
16
15
  from pathlib import Path
17
16
 
18
17
  from sarj_python_lint._file_context import PythonFileContext
18
+ from sarj_python_lint.rules._runtime_bindings import RuntimeBindings
19
19
 
20
20
 
21
- _SCOPES = (
22
- ast.Module,
23
- ast.FunctionDef,
24
- ast.AsyncFunctionDef,
25
- ast.ClassDef,
26
- ast.Lambda,
27
- ast.ListComp,
28
- ast.SetComp,
29
- ast.DictComp,
30
- ast.GeneratorExp,
31
- )
32
21
  _RESOURCE_OWNERS = MappingProxyType(
33
22
  {
34
23
  "builtins": frozenset({"open"}),
@@ -84,8 +73,9 @@ class ResourceProvenance:
84
73
  )
85
74
  self.parents = context.parents
86
75
  self.require_static_namespace = require_static_namespace
87
- self.bindings: dict[ast.AST | None, dict[str, list[ast.AST]]] = defaultdict(lambda: defaultdict(list))
88
- self.reads: dict[str, list[ast.Name]] = defaultdict(list)
76
+ self.inventory = context.binding_inventory
77
+ self.bindings = self.inventory.bindings
78
+ self.reads = self.inventory.reads
89
79
  self._reads_by_binding: dict[str, dict[ast.AST, list[ast.Name]]] = {}
90
80
  self._runtime_reads_by_binding: dict[str, dict[ast.AST, list[ast.Name]]] = {}
91
81
  self._import_read_safety: dict[str, bool] = {}
@@ -93,22 +83,10 @@ class ResourceProvenance:
93
83
  self.async_contract_cache: dict[ast.AsyncFunctionDef, bool] = {}
94
84
  self.import_contract_cache: dict[str, bool] = {}
95
85
  self.runtime_import_cache: dict[ast.alias, bool] = {}
96
- self.wildcard = False
97
- self.mutated: set[str] = set()
98
- self.external_bindings: set[str] = set()
99
- for node in context.nodes(ast.AST):
100
- if isinstance(node, ast.Name) and isinstance(node.ctx, ast.Load):
101
- self.reads[node.id].append(node)
102
-
103
- for name in _bound_names(node):
104
- self.bindings[self._binding_scope(node)][name].append(node)
105
- self.wildcard |= name == "*"
106
- if isinstance(node, ast.Global | ast.Nonlocal):
107
- self.external_bindings.update(node.names)
108
- if isinstance(node, ast.Attribute) and isinstance(node.ctx, ast.Store | ast.Del):
109
- root = _root_name(node)
110
- if root is not None:
111
- self.mutated.add(root)
86
+ self.wildcard = self.inventory.wildcard
87
+ # Some analyses refine mutation ownership for their own policy.
88
+ self.mutated = set(self.inventory.mutated)
89
+ self.external_bindings = self.inventory.external_bindings
112
90
 
113
91
  @property
114
92
  def context(self) -> PythonFileContext:
@@ -123,31 +101,7 @@ class ResourceProvenance:
123
101
  return builtin_namespace_untouched(self.context)
124
102
 
125
103
  def scope(self, node: ast.AST) -> ast.AST | None:
126
- parent = self.parents.get(node)
127
- while parent is not None:
128
- if isinstance(parent, ast.arguments) and (node in parent.defaults or node in parent.kw_defaults):
129
- parent = self.parents.get(parent)
130
- elif isinstance(parent, _SCOPES) and not (
131
- (
132
- isinstance(parent, ast.FunctionDef | ast.AsyncFunctionDef | ast.ClassDef)
133
- and node in parent.decorator_list
134
- )
135
- or (isinstance(parent, ast.ClassDef) and (node in parent.bases or node in parent.keywords))
136
- ):
137
- return parent
138
- if parent is None:
139
- return None
140
- node = parent
141
- parent = self.parents.get(parent)
142
- return None
143
-
144
- def _binding_scope(self, node: ast.AST) -> ast.AST | None:
145
- scope = self.scope(node)
146
- parent = self.parents.get(node)
147
- if isinstance(parent, ast.NamedExpr) and parent.target is node:
148
- while isinstance(scope, ast.ListComp | ast.SetComp | ast.DictComp | ast.GeneratorExp):
149
- scope = self.scope(scope)
150
- return scope
104
+ return self.inventory.scope(node)
151
105
 
152
106
  def binding(self, name: str, at: ast.AST) -> ast.AST | None:
153
107
  scope = self.scope(at)
@@ -160,13 +114,13 @@ class ResourceProvenance:
160
114
 
161
115
  @cached_property
162
116
  def runtime_bindings(self) -> RuntimeBindings:
163
- return RuntimeBindings(self.context, self.bindings)
117
+ return self.context.runtime_bindings
164
118
 
165
119
  def runtime_binding(self, name: str, at: ast.AST) -> ast.AST | None:
166
120
  nodes = self.runtime_bindings.lookup(self.scope(at), name)
167
121
  if not nodes:
168
122
  return None
169
- return nodes[0] if len(nodes) == 1 or self._same_import_bindings(list(nodes)) else None
123
+ return nodes[0] if len(nodes) == 1 or self._same_import_bindings(nodes) else None
170
124
 
171
125
  def runtime_import_name(self, node: ast.expr) -> str | None:
172
126
  root = _root_name(node)
@@ -265,7 +219,7 @@ class ResourceProvenance:
265
219
  self.context.path, module, level, facts=self.context.session.first_party
266
220
  )
267
221
 
268
- def _same_import_bindings(self, nodes: list[ast.AST]) -> bool:
222
+ def _same_import_bindings(self, nodes: Sequence[ast.AST]) -> bool:
269
223
  targets: set[tuple[str, str | None, int, str]] = set()
270
224
  for node in nodes:
271
225
  if not isinstance(node, ast.alias):
@@ -574,25 +528,3 @@ def import_name_for_binding(expression: ast.expr, binding: ast.alias, parent: as
574
528
  case _:
575
529
  return None
576
530
  return ".".join((prefix, *reversed(attributes)))
577
-
578
-
579
- def _bound_names(node: ast.AST) -> tuple[str, ...]:
580
- match node:
581
- case ast.Name(id=name, ctx=ast.Store() | ast.Del()) | ast.arg(arg=name):
582
- return (name,)
583
- case ast.alias():
584
- return (node.asname or node.name.partition(".")[0],)
585
- case (
586
- ast.FunctionDef(name=name)
587
- | ast.AsyncFunctionDef(name=name)
588
- | ast.ClassDef(name=name)
589
- | ast.ExceptHandler(name=str() as name)
590
- | ast.MatchAs(name=str() as name)
591
- | ast.MatchStar(name=str() as name)
592
- | ast.MatchMapping(rest=str() as name)
593
- ):
594
- return (name,)
595
- case ast.Global(names=names) | ast.Nonlocal(names=names):
596
- return tuple(names)
597
- case _:
598
- return ()