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.
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/PKG-INFO +1 -1
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/pyproject.toml +1 -1
- sarj_python_lint-1.62.0/src/sarj_python_lint/cast_access.py +86 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_registry.py +2 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/duplicate_adjacent_exception_handler.py +2 -4
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_any_mapping_types.py +95 -11
- sarj_python_lint-1.62.0/src/sarj_python_lint/rules/no_dual_shape_model_field_reader.py +389 -0
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +110 -14
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_str_enum.py +100 -6
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/unused_mock_setup.py +3 -3
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/.gitignore +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/LICENSE +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/README.md +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/_analysis_session.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/_file_context.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/_filesystem.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/_python_target.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/_source.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/benchmark.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/interpreter_argv.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/json_boundary.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_ast_position.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_closed_helpers.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_comments.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_copy_on_write.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_data_union_discriminator.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_docstrings.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_exports.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_fastapi.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_imports.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_inert_model_annotation.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_inherited_contract.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_local_source.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_mapping_shape_callbacks.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_namespace_access.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_native_namespace_capability.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_project_index.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_pydantic_field_contract.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_resolved_operation_signature.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_resource_provenance.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_runtime_bindings.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_set_fields.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_static_json_alias.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_store_writes.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_test_composition.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_test_provenance.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/_type_guard_symbols.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
- {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
- {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
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/duplicate_closed_pure_helper.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
- {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
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_async_function_cache.py +0 -0
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_broad_keyword_capture.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {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
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_contradictory_type_guard.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_discarded_validation_result.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
- {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
- {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
- {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
- {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
- {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
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_input_model_mutation.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_interpreter_source_arguments.py +0 -0
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_invalid_pydantic_bypass.py +0 -0
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_loguru_exc_info.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_loguru_printf_format.py +0 -0
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_mutating_container_transform.py +0 -0
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_partial_type_is.py +0 -0
- {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
- {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
- {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
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {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
- {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
- {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
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_test_method_grafting.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
- {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
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_unused_copy_result.py +0 -0
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
- {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
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_bulk_upsert.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_declarative_batch_validation.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_discriminated_model_union.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {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
- {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
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_native_string_check.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_psycopg_executemany.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_pytest_fixture_injection.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_required_constructor_parameters.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/redundant_test_constructor_forwarder.py +0 -0
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/repeated_unimplemented_test_contract.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/require_injectable_retry_sleep.py +0 -0
- {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
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/require_precise_factory_signature.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
- {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
- {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
- {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
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {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
- {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
- {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
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/test_without_explicit_oracle.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
- {sarj_python_lint-1.61.0 → sarj_python_lint-1.62.0}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
- {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.
|
|
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/
|
|
@@ -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 =
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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":
|