sarj-python-lint 1.61.0__tar.gz → 1.62.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/PKG-INFO +1 -1
  2. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/pyproject.toml +1 -1
  3. sarj_python_lint-1.62.0/src/sarj_python_lint/cast_access.py +86 -0
  4. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_registry.py +2 -0
  5. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/duplicate_adjacent_exception_handler.py +2 -4
  6. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_any_mapping_types.py +95 -11
  7. sarj_python_lint-1.62.0/src/sarj_python_lint/rules/no_dual_shape_model_field_reader.py +389 -0
  8. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +145 -0
  9. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +110 -14
  10. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_str_enum.py +100 -6
  11. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/unused_mock_setup.py +3 -3
  12. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/.gitignore +0 -0
  13. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/LICENSE +0 -0
  14. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/README.md +0 -0
  15. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/__init__.py +0 -0
  16. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/__main__.py +0 -0
  17. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/_analysis_session.py +0 -0
  18. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/_file_context.py +0 -0
  19. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/_filesystem.py +0 -0
  20. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/_python_target.py +0 -0
  21. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/_ratchet_cli.py +0 -0
  22. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/_secret_names.py +0 -0
  23. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/_source.py +0 -0
  24. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/_version.py +0 -0
  25. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/benchmark.py +0 -0
  26. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/interpreter_argv.py +0 -0
  27. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/json_boundary.py +0 -0
  28. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/py.typed +0 -0
  29. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/ratchet.py +0 -0
  30. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rule_base.py +0 -0
  31. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/__init__.py +0 -0
  32. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
  33. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_ast_index.py +0 -0
  34. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_ast_position.py +0 -0
  35. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_closed_helpers.py +0 -0
  36. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_comments.py +0 -0
  37. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
  38. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_copy_on_write.py +0 -0
  39. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_data_union_discriminator.py +0 -0
  40. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
  41. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_docstrings.py +0 -0
  42. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_exports.py +0 -0
  43. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_fastapi.py +0 -0
  44. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_first_party.py +0 -0
  45. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
  46. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_imports.py +0 -0
  47. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_inert_model_annotation.py +0 -0
  48. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_inherited_contract.py +0 -0
  49. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_local_source.py +0 -0
  50. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_logging.py +0 -0
  51. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_mapping_shape_callbacks.py +0 -0
  52. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_namespace_access.py +0 -0
  53. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_native_namespace_capability.py +0 -0
  54. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
  55. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_paths.py +0 -0
  56. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_project_index.py +0 -0
  57. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
  58. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_pydantic_field_contract.py +0 -0
  59. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_pytest.py +0 -0
  60. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_resolved_operation_signature.py +0 -0
  61. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_resource_provenance.py +0 -0
  62. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_runtime_bindings.py +0 -0
  63. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_set_fields.py +0 -0
  64. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_sql.py +0 -0
  65. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_static_json_alias.py +0 -0
  66. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_store_writes.py +0 -0
  67. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
  68. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
  69. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_test_composition.py +0 -0
  70. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_test_provenance.py +0 -0
  71. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_type_guard_symbols.py +0 -0
  72. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
  73. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
  74. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
  75. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
  76. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
  77. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
  78. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/duplicate_closed_pure_helper.py +0 -0
  79. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
  80. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
  81. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
  82. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
  83. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
  84. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
  85. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
  86. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
  87. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
  88. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
  89. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_async_function_cache.py +0 -0
  90. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_before_validator_input_mutation.py +0 -0
  91. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_broad_keyword_capture.py +0 -0
  92. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
  93. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_conflicting_pydantic_alias_validation.py +0 -0
  94. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
  95. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_contradictory_type_guard.py +0 -0
  96. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
  97. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
  98. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
  99. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
  100. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_discarded_validation_result.py +0 -0
  101. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
  102. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
  103. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
  104. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
  105. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
  106. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
  107. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
  108. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_fixed_sleep_in_loop.py +0 -0
  109. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
  110. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
  111. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
  112. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_impossible_pydantic_field_bounds.py +0 -0
  113. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
  114. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_input_model_mutation.py +0 -0
  115. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_interpreter_source_arguments.py +0 -0
  116. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
  117. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_invalid_pydantic_bypass.py +0 -0
  118. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_invalid_pydantic_validator_return.py +0 -0
  119. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_loguru_exc_info.py +0 -0
  120. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_loguru_printf_format.py +0 -0
  121. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_mocked_pydantic_value_object.py +0 -0
  122. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_mutating_container_transform.py +0 -0
  123. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
  124. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
  125. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
  126. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_partial_type_is.py +0 -0
  127. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
  128. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
  129. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
  130. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
  131. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
  132. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
  133. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
  134. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
  135. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
  136. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
  137. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_select_star.py +0 -0
  138. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
  139. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_shallow_container_type_guard.py +0 -0
  140. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_shallow_record_type_guard.py +0 -0
  141. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
  142. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
  143. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
  144. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_test_method_grafting.py +0 -0
  145. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
  146. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
  147. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_unknown_pydantic_validator_field.py +0 -0
  148. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
  149. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_unused_copy_result.py +0 -0
  150. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_unused_underscored_keyword_parameter.py +0 -0
  151. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
  152. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
  153. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
  154. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
  155. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
  156. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
  157. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_autospec_for_callable_mock.py +0 -0
  158. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_bulk_upsert.py +0 -0
  159. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
  160. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
  161. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
  162. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_declarative_batch_validation.py +0 -0
  163. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_discriminated_model_union.py +0 -0
  164. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
  165. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
  166. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
  167. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
  168. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
  169. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
  170. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
  171. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
  172. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
  173. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
  174. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
  175. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
  176. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_native_string_check.py +0 -0
  177. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
  178. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
  179. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_psycopg_executemany.py +0 -0
  180. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
  181. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_pytest_fixture_injection.py +0 -0
  182. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
  183. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_required_constructor_parameters.py +0 -0
  184. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
  185. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
  186. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
  187. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
  188. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
  189. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
  190. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
  191. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
  192. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
  193. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
  194. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
  195. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
  196. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
  197. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/redundant_test_constructor_forwarder.py +0 -0
  198. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
  199. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
  200. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
  201. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/repeated_unimplemented_test_contract.py +0 -0
  202. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
  203. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
  204. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
  205. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/require_injectable_retry_sleep.py +0 -0
  206. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
  207. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
  208. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
  209. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/require_precise_factory_signature.py +0 -0
  210. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
  211. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
  212. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/require_pydantic_for_mapping_shape.py +0 -0
  213. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
  214. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
  215. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
  216. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
  217. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/stepdown.py +0 -0
  218. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/store_field_update_reuses_upsert.py +0 -0
  219. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
  220. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
  221. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
  222. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/test_without_explicit_oracle.py +0 -0
  223. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
  224. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
  225. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
  226. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
  227. {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/unused_test_factory_option.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sarj-python-lint
3
- Version: 1.61.0
3
+ Version: 1.62.0
4
4
  Summary: Custom Python lint rules — AST-based, pre-commit-friendly, hypermodern defaults
5
5
  Project-URL: Homepage, https://code-standards.sarj.ai/rules/python/
6
6
  Project-URL: Documentation, https://code-standards.sarj.ai/rules/python/
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sarj-python-lint"
3
- version = "1.61.0"
3
+ version = "1.62.0"
4
4
  description = "Custom Python lint rules — AST-based, pre-commit-friendly, hypermodern defaults"
5
5
  readme = "README.md"
6
6
  authors = [{ name = "sarj-ai" }]
@@ -0,0 +1,86 @@
1
+ from __future__ import annotations
2
+
3
+ import ast
4
+ from dataclasses import dataclass
5
+ from typing import TYPE_CHECKING, Literal
6
+
7
+ from sarj_python_lint._file_context import PythonFileContext
8
+ from sarj_python_lint.rules._resource_provenance import ResourceProvenance
9
+
10
+
11
+ if TYPE_CHECKING:
12
+ from pathlib import Path
13
+
14
+
15
+ _TYPING = frozenset({"typing", "typing_extensions"})
16
+ _GETATTR_ARITIES = frozenset({2, 3})
17
+
18
+
19
+ @dataclass(frozen=True, slots=True)
20
+ class CastAccess:
21
+ node: ast.Call | ast.ImportFrom
22
+ qualified_api: str
23
+ kind: Literal["wildcard", "reflection"]
24
+
25
+
26
+ def indirect_typing_cast_accesses(path: Path, source: str) -> tuple[CastAccess, ...]:
27
+ context = PythonFileContext(path, source)
28
+ if "*" not in source and "getattr" not in context.symbol_source:
29
+ return ()
30
+ if context.tree is None:
31
+ return ()
32
+ accesses = [
33
+ CastAccess(node, f"{node.module}.cast", "wildcard")
34
+ for node in context.nodes(ast.ImportFrom)
35
+ if node.level == 0 and node.module in _TYPING and any(alias.name == "*" for alias in node.names)
36
+ ]
37
+ provenance = ResourceProvenance(context, require_static_namespace=True)
38
+ if provenance.wildcard or provenance.dynamic_namespace or not provenance.builtin_namespace_untouched:
39
+ return tuple(accesses)
40
+ for call in context.nodes(ast.Call):
41
+ if not _getattr_call(call, provenance):
42
+ continue
43
+ module = _typing_receiver(call.args[0], provenance)
44
+ if module is not None:
45
+ accesses.append(CastAccess(call, f"{module}.cast", "reflection"))
46
+ return tuple(sorted(accesses, key=lambda access: (access.node.lineno, access.node.col_offset)))
47
+
48
+
49
+ def _getattr_call(call: ast.Call, provenance: ResourceProvenance) -> bool:
50
+ if not (
51
+ isinstance(call.func, ast.Name)
52
+ and call.func.id == "getattr"
53
+ and len(call.args) in _GETATTR_ARITIES
54
+ and not call.keywords
55
+ and isinstance(call.args[1], ast.Constant)
56
+ and call.args[1].value == "cast"
57
+ ):
58
+ return False
59
+ scope = provenance.scope(call.func)
60
+ while scope is not None:
61
+ if "getattr" in provenance.bindings.get(scope, {}):
62
+ return False
63
+ scope = provenance.scope(scope)
64
+ return True
65
+
66
+
67
+ def _typing_receiver(node: ast.expr, provenance: ResourceProvenance) -> str | None:
68
+ if not isinstance(node, ast.Name) or node.id in provenance.mutated | provenance.external_bindings:
69
+ return None
70
+ binding = provenance.binding(node.id, node)
71
+ if not (
72
+ isinstance(binding, ast.alias)
73
+ and isinstance(provenance.context.parents.get(binding), ast.Import)
74
+ and binding.name in _TYPING
75
+ ):
76
+ return None
77
+ for read in provenance.reads.get(node.id, ()):
78
+ if provenance.binding(read.id, read) is not binding:
79
+ continue
80
+ parent = provenance.context.parents.get(read)
81
+ if isinstance(parent, ast.Attribute) and parent.value is read:
82
+ continue
83
+ if isinstance(parent, ast.Call) and _getattr_call(parent, provenance) and parent.args[0] is read:
84
+ continue
85
+ return None
86
+ return binding.name
@@ -44,6 +44,7 @@ from sarj_python_lint.rules.no_cors_wildcard_with_credentials import (
44
44
  from sarj_python_lint.rules.no_database_programmability import NoDatabaseProgrammability
45
45
  from sarj_python_lint.rules.no_delete_statement import NoDeleteStatement
46
46
  from sarj_python_lint.rules.no_discarded_validation_result import NoDiscardedValidationResult
47
+ from sarj_python_lint.rules.no_dual_shape_model_field_reader import NoDualShapeModelFieldReader
47
48
  from sarj_python_lint.rules.no_dunder_all import NoDunderAll
48
49
  from sarj_python_lint.rules.no_duplicate_dunder_all_entry import NoDuplicateDunderAllEntry
49
50
  from sarj_python_lint.rules.no_excessive_cognitive_complexity import NoExcessiveCognitiveComplexity
@@ -307,6 +308,7 @@ REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
307
308
  RequireKeywordOnlySwapProneParams.id: RequireKeywordOnlySwapProneParams,
308
309
  RequirePydanticForExternalJson.id: RequirePydanticForExternalJson,
309
310
  RequirePydanticForMappingShape.id: RequirePydanticForMappingShape,
311
+ NoDualShapeModelFieldReader.id: NoDualShapeModelFieldReader,
310
312
  PreferModuleLevelConstant.id: PreferModuleLevelConstant,
311
313
  PreferImmutableModuleConstant.id: PreferImmutableModuleConstant,
312
314
  PreferInjectedDependencyOverMonkeypatch.id: PreferInjectedDependencyOverMonkeypatch,
@@ -3,10 +3,10 @@ from __future__ import annotations
3
3
  import ast
4
4
  import builtins
5
5
  from pathlib import PurePosixPath
6
+ from types import MappingProxyType
6
7
  from typing import (
7
8
  TYPE_CHECKING,
8
9
  ClassVar,
9
- cast, # ruff: ignore[banned-api] -- vars exposes Any; the object view permits isinstance narrowing.
10
10
  final,
11
11
  override,
12
12
  )
@@ -31,14 +31,12 @@ from sarj_python_lint.rules._suppression_comments import scan_comments
31
31
 
32
32
 
33
33
  if TYPE_CHECKING:
34
- from collections.abc import Mapping
35
-
36
34
  from sarj_python_lint._file_context import PythonFileContext
37
35
  from sarj_python_lint.rules._project_index import SourceUnit, SymbolRef
38
36
  from sarj_python_lint.rules._suppression_comments import Comment
39
37
 
40
38
 
41
- _BUILTIN_NAMESPACE = cast("Mapping[str, object]", vars(builtins))
39
+ _BUILTIN_NAMESPACE = MappingProxyType[str, object](vars(builtins))
42
40
  _BUILTIN_EXCEPTIONS = frozenset(
43
41
  name for name, value in _BUILTIN_NAMESPACE.items() if isinstance(value, type) and issubclass(value, Exception)
44
42
  )
@@ -117,6 +117,34 @@ class NoAnyMappingTypes(Rule):
117
117
  expected_count=0,
118
118
  public=True,
119
119
  ),
120
+ RuleExample(
121
+ example_id="generic-any-binding",
122
+ scenario="provider-identity",
123
+ title="A generic parameter named Any keeps its own identity",
124
+ outcome=ExampleOutcome.NO_MATCH,
125
+ files=(
126
+ ExampleFile.python(
127
+ "app/service.py", "from typing import Any\nclass Rows[Any]:\n rows: dict[str, Any]\n"
128
+ ),
129
+ ),
130
+ focus_path=PurePosixPath("app/service.py"),
131
+ expected_count=0,
132
+ public=True,
133
+ ),
134
+ RuleExample(
135
+ example_id="generic-typing-any",
136
+ scenario="provider-identity",
137
+ title="An unrelated generic parameter preserves actual Any erasure",
138
+ outcome=ExampleOutcome.MATCH,
139
+ files=(
140
+ ExampleFile.python(
141
+ "app/service.py", "from typing import Any\nclass Rows[T]:\n rows: dict[str, Any]\n"
142
+ ),
143
+ ),
144
+ focus_path=PurePosixPath("app/service.py"),
145
+ expected_count=1,
146
+ public=True,
147
+ ),
120
148
  ),
121
149
  )
122
150
  description = documentation.summary
@@ -236,11 +264,12 @@ def _unshadowed_any_mappings(
236
264
  if parsed is None:
237
265
  return []
238
266
  names = {node.id for node in walk_ast(parsed) if isinstance(node, ast.Name)}
239
- if not names.isdisjoint(_shadowed_names(owner, parents, scope_cache)):
267
+ type_parameters = frozenset(_generic_scope_names(owner, parents))
268
+ if not names.isdisjoint(_shadowed_names(owner, parents, scope_cache) - type_parameters):
240
269
  return []
241
270
  return [
242
271
  (mapping, expression if isinstance(expression, ast.Constant) else mapping)
243
- for mapping in _any_mappings(expression, semantics)
272
+ for mapping in _any_mappings(expression, semantics, shadowed=type_parameters)
244
273
  ]
245
274
 
246
275
 
@@ -546,12 +575,39 @@ def _unshadowed_type_expression(
546
575
  return names.isdisjoint(_shadowed_names(node, parents, scope_cache))
547
576
 
548
577
 
578
+ def _generic_scope_names(node: ast.AST, parents: dict[int, ast.AST]) -> set[str]:
579
+ names = _type_parameter_names(node)
580
+ below = node
581
+ parent = parents.get(id(below))
582
+ function_default = False
583
+ while parent is not None:
584
+ match parent:
585
+ case ast.arguments():
586
+ function_default = below in parent.defaults or below in parent.kw_defaults
587
+ case ast.FunctionDef() | ast.AsyncFunctionDef():
588
+ if not function_default and below not in parent.decorator_list:
589
+ names.update(_type_parameter_names(parent))
590
+ function_default = False
591
+ case ast.Lambda():
592
+ function_default = False
593
+ case ast.ClassDef():
594
+ if below not in parent.decorator_list:
595
+ names.update(_type_parameter_names(parent))
596
+ case ast.TypeAlias():
597
+ names.update(_type_parameter_names(parent))
598
+ case _:
599
+ pass
600
+ below = parent
601
+ parent = parents.get(id(below))
602
+ return names
603
+
604
+
549
605
  def _shadowed_names(
550
606
  node: ast.AST,
551
607
  parents: dict[int, ast.AST],
552
608
  scope_cache: dict[int, frozenset[str]],
553
609
  ) -> set[str]:
554
- shadowed: set[str] = set()
610
+ shadowed = _generic_scope_names(node, parents)
555
611
  crossed_function_body = False
556
612
  below = node
557
613
  parent = parents.get(id(below))
@@ -567,6 +623,16 @@ def _shadowed_names(
567
623
  return shadowed
568
624
 
569
625
 
626
+ def _type_parameter_names(node: ast.AST) -> set[str]:
627
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef, ast.TypeAlias)):
628
+ return {
629
+ parameter.name
630
+ for parameter in node.type_params
631
+ if isinstance(parameter, (ast.TypeVar, ast.ParamSpec, ast.TypeVarTuple))
632
+ }
633
+ return set()
634
+
635
+
570
636
  def _scope_names(
571
637
  owner: ast.FunctionDef | ast.AsyncFunctionDef | ast.ClassDef,
572
638
  cache: dict[int, frozenset[str]],
@@ -628,7 +694,9 @@ def _type_comment(comment: str, owner: ast.AST) -> ast.expr | None:
628
694
  return expression
629
695
 
630
696
 
631
- def _any_mappings(annotation: ast.expr, semantics: AnnotationSemantics) -> list[ast.Subscript]:
697
+ def _any_mappings(
698
+ annotation: ast.expr, semantics: AnnotationSemantics, *, shadowed: frozenset[str] = frozenset()
699
+ ) -> list[ast.Subscript]:
632
700
  resolved = semantics.parse(annotation)
633
701
  if resolved is None:
634
702
  return []
@@ -636,24 +704,30 @@ def _any_mappings(annotation: ast.expr, semantics: AnnotationSemantics) -> list[
636
704
  mappings: list[ast.Subscript] = []
637
705
  while pending:
638
706
  node = pending.pop()
639
- if isinstance(node, ast.Subscript) and _is_any_mapping(node, semantics):
707
+ if isinstance(node, ast.Subscript) and _is_any_mapping(node, semantics, shadowed=shadowed):
640
708
  mappings.append(node)
641
- pending.extend(_type_children(node, semantics))
709
+ pending.extend(_type_children(node, semantics, shadowed=shadowed))
642
710
  return mappings
643
711
 
644
712
 
645
- def _is_any_mapping(node: ast.Subscript, semantics: AnnotationSemantics) -> bool:
713
+ def _is_any_mapping(node: ast.Subscript, semantics: AnnotationSemantics, *, shadowed: frozenset[str]) -> bool:
646
714
  if not isinstance(node.slice, ast.Tuple) or len(node.slice.elts) != _MAPPING_ARGUMENT_COUNT:
647
715
  return False
648
716
  imports = semantics.imports
649
717
  target = node.value
718
+ if _expression_root(target) in shadowed:
719
+ return False
650
720
  mapping = (isinstance(target, ast.Name) and target.id == "dict" and imports.builtin_is_unshadowed("dict")) or any(
651
721
  imports.resolves(target, sources=_MAPPING_SOURCES, symbol=name) for name in _MAPPING_NAMES
652
722
  )
653
723
  if not mapping:
654
724
  return False
655
725
  key, value = node.slice.elts
656
- return _is_builtin_str(key, imports) and _contains_any(value, semantics, seen=frozenset())
726
+ return (
727
+ _expression_root(key) not in shadowed
728
+ and _is_builtin_str(key, imports)
729
+ and _contains_any(value, semantics, seen=frozenset(), shadowed=shadowed)
730
+ )
657
731
 
658
732
 
659
733
  def _is_builtin_str(node: ast.expr, imports: ImportIndex) -> bool:
@@ -665,20 +739,30 @@ def _contains_any(
665
739
  semantics: AnnotationSemantics,
666
740
  *,
667
741
  seen: frozenset[str],
742
+ shadowed: frozenset[str] = frozenset(),
668
743
  ) -> bool:
669
744
  parsed = semantics.parse(node)
670
- if parsed is None:
745
+ if parsed is None or _expression_root(parsed) in shadowed:
671
746
  return False
672
747
  imports = semantics.imports
673
748
  if imports.resolves(parsed, sources=_TYPING_SOURCES, symbol="Any"):
674
749
  return True
675
750
  if isinstance(parsed, ast.Name) and parsed.id in semantics.aliases and parsed.id not in seen:
751
+ # Module aliases retain their declaration scope, independent of a
752
+ # generic name at this annotation's use site.
676
753
  return _contains_any(semantics.aliases[parsed.id], semantics, seen=seen | {parsed.id})
677
- return any(_contains_any(child, semantics, seen=seen) for child in _type_children(parsed, semantics))
754
+ return any(
755
+ _contains_any(child, semantics, seen=seen, shadowed=shadowed)
756
+ for child in _type_children(parsed, semantics, shadowed=shadowed)
757
+ )
678
758
 
679
759
 
680
- def _type_children(node: ast.expr, semantics: AnnotationSemantics) -> tuple[ast.expr, ...]:
760
+ def _type_children(
761
+ node: ast.expr, semantics: AnnotationSemantics, *, shadowed: frozenset[str] = frozenset()
762
+ ) -> tuple[ast.expr, ...]:
681
763
  if isinstance(node, ast.Subscript):
764
+ if _expression_root(node.value) in shadowed:
765
+ return ()
682
766
  imports = semantics.imports
683
767
  symbol = imports.resolved_symbol(node.value, sources=_TYPING_SOURCES | _MAPPING_SOURCES)
684
768
  if symbol == "Literal":