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.
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/PKG-INFO +1 -1
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/pyproject.toml +1 -1
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_enums.py +1 -1
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_repeated.py +1 -1
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_file_context.py +11 -1
- sarj_python_lint-1.83.1/src/sarj_python_lint/rules/_binding_inventory.py +131 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_closed_helpers.py +1 -1
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_data_union_discriminator.py +3 -1
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_project_index.py +65 -56
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_resource_provenance.py +13 -81
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_interpreter_source_arguments.py +39 -22
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_redundant_local_alias.py +1 -1
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_required_constructor_parameters.py +10 -8
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/unused_mock_setup.py +12 -1
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/.gitignore +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/LICENSE +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/README.md +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_analysis_session.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_aliases.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_annotations.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_comments.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_declared_contracts.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_declared_tests.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_docs.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_exhaustiveness.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_inert.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_parameters.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_security.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_sql_projection.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_tests.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_filesystem.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_fixes.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_parallel.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_python_target.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_source.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/application_logging.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/autofix.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/benchmark.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/cast_access.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/interpreter_argv.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/json_boundary.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_annotation_origins.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_ast_names.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_ast_position.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_async_lifecycle.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_comment_narration.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_comments.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_copy_on_write.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_declared_contracts.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_docstrings.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_exports.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_fastapi.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_imports.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_inert_model_annotation.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_inherited_contract.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_local_alias_fix.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_local_source.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_mapping_shape_callbacks.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_namespace_access.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_native_namespace_capability.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_native_object_contracts.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_numeric_proof.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_opaque_contracts.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_pydantic_field_contract.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_registry.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_resolved_operation_signature.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_route_shapes.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_runtime_bindings.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_service_http_callbacks.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_set_fields.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_sql_aggregate_identity.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_sql_explain.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_sql_policy_source.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_static_json_alias.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_store_writes.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_test_composition.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_test_provenance.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_transactional_sql.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_type_guard_symbols.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/async_executor_shutdown.py +0 -0
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/async_subprocess_cancellation_cleanup.py +0 -0
- {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
- {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
- {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
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/duplicate_adjacent_exception_handler.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/duplicate_closed_pure_helper.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/duplicate_pytest_fixture_params.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/failed_probe_zero_threshold.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
- {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
- {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
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_async_function_cache.py +0 -0
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_blanket_suppression.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_broad_keyword_capture.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {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
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_contradictory_type_guard.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
- {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
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_defaulted_environment_setting.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_discarded_validation_result.py +0 -0
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
- {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
- {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
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_hasattr.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_input_model_mutation.py +0 -0
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_invalid_pydantic_bypass.py +0 -0
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_loguru_exc_info.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_loguru_printf_format.py +0 -0
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_mutating_container_transform.py +0 -0
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_partial_type_is.py +0 -0
- {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
- {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
- {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
- {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
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {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
- {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
- {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
- {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
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_test_method_grafting.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
- {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
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_unused_copy_result.py +0 -0
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
- {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
- {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
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_bulk_upsert.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_declarative_batch_validation.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_direct_import_module.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_discriminated_model_union.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_grouped_settings.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {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
- {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
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_native_string_check.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_precise_object_contract.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_psycopg_executemany.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_pydantic_model_assertion.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_pytest_dependency_fixtures.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_pytest_fixture_injection.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_typed_settings_field.py +0 -0
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
- {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
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/redundant_test_constructor_forwarder.py +0 -0
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/repeated_unimplemented_test_contract.py +0 -0
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/require_injectable_retry_sleep.py +0 -0
- {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
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/require_precise_factory_signature.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
- {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
- {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
- {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
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {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
- {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
- {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
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/test_without_explicit_oracle.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
- {sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/unused_test_factory_option.py +0 -0
- {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.
|
|
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/
|
|
@@ -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,
|
|
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(
|
{sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/_autofix_repeated.py
RENAMED
|
@@ -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) ==
|
|
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 ()
|
{sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_closed_helpers.py
RENAMED
|
@@ -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) ==
|
|
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:
|
|
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)
|
{sarj_python_lint-1.83.0 → sarj_python_lint-1.83.1}/src/sarj_python_lint/rules/_project_index.py
RENAMED
|
@@ -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
|
-
|
|
554
|
-
|
|
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
|
-
|
|
559
|
-
|
|
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
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
if
|
|
592
|
-
|
|
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
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
if
|
|
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
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
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
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
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
|
|
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)
|
|
1380
|
-
|
|
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
|
-
|
|
1385
|
-
parameter.arg not in {"self", "cls"}
|
|
1386
|
-
|
|
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
|
|
1458
|
+
def _annotation_symbols(unit: SourceUnit, annotation: ast.expr | None) -> Iterator[SymbolRef]:
|
|
1448
1459
|
if annotation is None:
|
|
1449
|
-
return
|
|
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
|
-
|
|
1454
|
-
_resolve(unit, candidate)
|
|
1455
|
-
|
|
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.
|
|
88
|
-
self.
|
|
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 =
|
|
97
|
-
|
|
98
|
-
self.
|
|
99
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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:
|
|
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 ()
|