sarj-python-lint 1.48.0__tar.gz → 1.49.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.48.0 → sarj_python_lint-1.49.0}/PKG-INFO +1 -1
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/pyproject.toml +1 -1
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_registry.py +2 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_resource_provenance.py +126 -0
- sarj_python_lint-1.49.0/src/sarj_python_lint/rules/_runtime_bindings.py +113 -0
- sarj_python_lint-1.49.0/src/sarj_python_lint/rules/no_discarded_validation_result.py +453 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/.gitignore +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/LICENSE +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/README.md +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/_analysis_session.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/_file_context.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/_filesystem.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/_python_target.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/_source.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/benchmark.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/interpreter_argv.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/json_boundary.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_ast_position.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_closed_helpers.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_comments.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_copy_on_write.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_data_union_discriminator.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_docstrings.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_exports.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_fastapi.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_imports.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_inert_model_annotation.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_inherited_contract.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_local_source.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_namespace_access.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_native_namespace_capability.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_project_index.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_pydantic_field_contract.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_resolved_operation_signature.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_set_fields.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_static_json_alias.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_store_writes.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_test_composition.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_test_provenance.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/_type_guard_symbols.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/duplicate_adjacent_exception_handler.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/duplicate_closed_pure_helper.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_async_function_cache.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_before_validator_input_mutation.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_broad_keyword_capture.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_conflicting_pydantic_alias_validation.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_contradictory_type_guard.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_fixed_sleep_in_loop.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_impossible_pydantic_field_bounds.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_input_model_mutation.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_interpreter_source_arguments.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_invalid_pydantic_bypass.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_invalid_pydantic_validator_return.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_loguru_exc_info.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_loguru_printf_format.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_mocked_pydantic_value_object.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_partial_type_is.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_shallow_container_type_guard.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_shallow_record_type_guard.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_test_method_grafting.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_unknown_pydantic_validator_field.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_unused_copy_result.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_unused_underscored_keyword_parameter.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_autospec_for_callable_mock.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_bulk_upsert.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_declarative_batch_validation.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_discriminated_model_union.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_native_string_check.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_psycopg_executemany.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_pytest_fixture_injection.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_required_constructor_parameters.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/redundant_test_constructor_forwarder.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/repeated_unimplemented_test_contract.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/require_injectable_retry_sleep.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/require_precise_factory_signature.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/store_field_update_reuses_upsert.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/test_without_explicit_oracle.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.0}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
- {sarj_python_lint-1.48.0 → sarj_python_lint-1.49.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.49.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/
|
|
@@ -43,6 +43,7 @@ from sarj_python_lint.rules.no_cors_wildcard_with_credentials import (
|
|
|
43
43
|
)
|
|
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
|
+
from sarj_python_lint.rules.no_discarded_validation_result import NoDiscardedValidationResult
|
|
46
47
|
from sarj_python_lint.rules.no_dunder_all import NoDunderAll
|
|
47
48
|
from sarj_python_lint.rules.no_duplicate_dunder_all_entry import NoDuplicateDunderAllEntry
|
|
48
49
|
from sarj_python_lint.rules.no_excessive_cognitive_complexity import NoExcessiveCognitiveComplexity
|
|
@@ -249,6 +250,7 @@ REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
|
|
|
249
250
|
NoFixedSleepInLoop.id: NoFixedSleepInLoop,
|
|
250
251
|
NoInterpreterSourceArguments.id: NoInterpreterSourceArguments,
|
|
251
252
|
NoUnusedCopyResult.id: NoUnusedCopyResult,
|
|
253
|
+
NoDiscardedValidationResult.id: NoDiscardedValidationResult,
|
|
252
254
|
NoBeforeValidatorInputMutation.id: NoBeforeValidatorInputMutation,
|
|
253
255
|
NoInputModelMutation.id: NoInputModelMutation,
|
|
254
256
|
NoDatabaseProgrammability.id: NoDatabaseProgrammability,
|
|
@@ -8,9 +8,12 @@ from typing import TYPE_CHECKING, final
|
|
|
8
8
|
|
|
9
9
|
from sarj_python_lint.rules._ast_index import has_own_yield
|
|
10
10
|
from sarj_python_lint.rules._namespace_access import dynamic_namespace_access
|
|
11
|
+
from sarj_python_lint.rules._runtime_bindings import RuntimeBindings
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
if TYPE_CHECKING:
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
14
17
|
from sarj_python_lint._file_context import PythonFileContext
|
|
15
18
|
|
|
16
19
|
|
|
@@ -76,6 +79,7 @@ class ResourceProvenance:
|
|
|
76
79
|
self.escape_cache: dict[tuple[ast.AST, bool], bool] = {}
|
|
77
80
|
self.async_contract_cache: dict[ast.AsyncFunctionDef, bool] = {}
|
|
78
81
|
self.import_contract_cache: dict[str, bool] = {}
|
|
82
|
+
self.runtime_import_cache: dict[ast.alias, bool] = {}
|
|
79
83
|
self.wildcard = False
|
|
80
84
|
self.mutated: set[str] = set()
|
|
81
85
|
self.external_bindings: set[str] = set()
|
|
@@ -133,6 +137,113 @@ class ResourceProvenance:
|
|
|
133
137
|
scope = self.scope(scope)
|
|
134
138
|
return None
|
|
135
139
|
|
|
140
|
+
@cached_property
|
|
141
|
+
def runtime_bindings(self) -> RuntimeBindings:
|
|
142
|
+
return RuntimeBindings(self.context, self.bindings)
|
|
143
|
+
|
|
144
|
+
def runtime_binding(self, name: str, at: ast.AST) -> ast.AST | None:
|
|
145
|
+
nodes = self.runtime_bindings.lookup(self.scope(at), name)
|
|
146
|
+
if not nodes:
|
|
147
|
+
return None
|
|
148
|
+
return nodes[0] if len(nodes) == 1 or self._same_import_bindings(list(nodes)) else None
|
|
149
|
+
|
|
150
|
+
def runtime_import_name(self, node: ast.expr) -> str | None:
|
|
151
|
+
root = _root_name(node)
|
|
152
|
+
if self.wildcard or self.dynamic_namespace or root is None or root in self.mutated:
|
|
153
|
+
return None
|
|
154
|
+
binding = self.runtime_binding(root, node)
|
|
155
|
+
if not isinstance(binding, ast.alias):
|
|
156
|
+
return None
|
|
157
|
+
qualified = _runtime_import_name(node, binding, self.context.parents.get(binding))
|
|
158
|
+
if qualified is None or not self._owner_namespace_untouched(qualified.partition(".")[0]):
|
|
159
|
+
return None
|
|
160
|
+
if binding not in self.runtime_import_cache:
|
|
161
|
+
self.runtime_import_cache[binding] = all(
|
|
162
|
+
self.runtime_binding(root, read) is not binding or self._safe_import_use(read)
|
|
163
|
+
for read in self.reads.get(root, ())
|
|
164
|
+
)
|
|
165
|
+
return qualified if self.runtime_import_cache[binding] else None
|
|
166
|
+
|
|
167
|
+
@cached_property
|
|
168
|
+
def mock_namespace_patching(self) -> bool:
|
|
169
|
+
return any(
|
|
170
|
+
self.runtime_import_name(node.func)
|
|
171
|
+
in {
|
|
172
|
+
"unittest.mock.patch",
|
|
173
|
+
"unittest.mock.patch.object",
|
|
174
|
+
"unittest.mock.patch.multiple",
|
|
175
|
+
"unittest.mock.patch.dict",
|
|
176
|
+
}
|
|
177
|
+
for node in self.context.nodes(ast.Call)
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
@cached_property
|
|
181
|
+
def imported_namespace_hazards(self) -> frozenset[Path]:
|
|
182
|
+
hazards: set[Path] = set()
|
|
183
|
+
for node in self.context.nodes(ast.Name, ast.Attribute):
|
|
184
|
+
match node:
|
|
185
|
+
case ast.Attribute(value=value, ctx=ast.Store() | ast.Del()):
|
|
186
|
+
hazards.update(self._namespace_paths(value, include_root=True))
|
|
187
|
+
case ast.Name(ctx=ast.Load()) | ast.Attribute(ctx=ast.Load()) if not self._safe_import_use(node):
|
|
188
|
+
hazards.update(self._namespace_paths(node, include_root=False))
|
|
189
|
+
case _:
|
|
190
|
+
pass
|
|
191
|
+
return frozenset(hazards)
|
|
192
|
+
|
|
193
|
+
@cached_property
|
|
194
|
+
def _namespace_aliases(self) -> dict[str, tuple[ast.alias, ...]]:
|
|
195
|
+
aliases: dict[str, list[ast.alias]] = defaultdict(list)
|
|
196
|
+
for binding in self.context.nodes(ast.alias):
|
|
197
|
+
parent = self.context.parents.get(binding)
|
|
198
|
+
name = binding.asname or (
|
|
199
|
+
binding.name.partition(".")[0] if isinstance(parent, ast.Import) else binding.name
|
|
200
|
+
)
|
|
201
|
+
aliases[name].append(binding)
|
|
202
|
+
return {name: tuple(bindings) for name, bindings in aliases.items()}
|
|
203
|
+
|
|
204
|
+
def _namespace_paths(self, expression: ast.expr, *, include_root: bool) -> set[Path]:
|
|
205
|
+
attributes: list[str] = []
|
|
206
|
+
root = expression
|
|
207
|
+
while isinstance(root, ast.Attribute):
|
|
208
|
+
attributes.append(root.attr)
|
|
209
|
+
root = root.value
|
|
210
|
+
if not isinstance(root, ast.Name):
|
|
211
|
+
return set()
|
|
212
|
+
binding = self.runtime_binding(root.id, expression)
|
|
213
|
+
aliases: tuple[ast.alias, ...]
|
|
214
|
+
match binding:
|
|
215
|
+
case ast.alias():
|
|
216
|
+
aliases = (binding,)
|
|
217
|
+
case None:
|
|
218
|
+
aliases = self._namespace_aliases.get(root.id, ())
|
|
219
|
+
case _:
|
|
220
|
+
return set()
|
|
221
|
+
paths: set[Path] = set()
|
|
222
|
+
for alias in aliases:
|
|
223
|
+
path = self._namespace_module_path(alias, tuple(reversed(attributes)))
|
|
224
|
+
if path is not None:
|
|
225
|
+
paths.add(path)
|
|
226
|
+
if include_root and attributes:
|
|
227
|
+
base = self._namespace_module_path(alias, ())
|
|
228
|
+
if base is not None:
|
|
229
|
+
paths.add(base)
|
|
230
|
+
return paths
|
|
231
|
+
|
|
232
|
+
def _namespace_module_path(self, binding: ast.alias, attributes: tuple[str, ...]) -> Path | None:
|
|
233
|
+
parent = self.context.parents.get(binding)
|
|
234
|
+
match parent:
|
|
235
|
+
case ast.Import():
|
|
236
|
+
prefix = binding.name if binding.asname else binding.name.partition(".")[0]
|
|
237
|
+
level = 0
|
|
238
|
+
case ast.ImportFrom(module=module, level=level):
|
|
239
|
+
prefix = ".".join(part for part in (module, binding.name) if part)
|
|
240
|
+
case _:
|
|
241
|
+
return None
|
|
242
|
+
module = ".".join((prefix, *attributes))
|
|
243
|
+
return self.context.session.local_source.resolve_module(
|
|
244
|
+
self.context.path, module, level, facts=self.context.session.first_party
|
|
245
|
+
)
|
|
246
|
+
|
|
136
247
|
def _same_import_bindings(self, nodes: list[ast.AST]) -> bool:
|
|
137
248
|
targets: set[tuple[str, str | None, int, str]] = set()
|
|
138
249
|
for node in nodes:
|
|
@@ -417,6 +528,21 @@ def _root_name(node: ast.expr) -> str | None:
|
|
|
417
528
|
return node.id if isinstance(node, ast.Name) else None
|
|
418
529
|
|
|
419
530
|
|
|
531
|
+
def _runtime_import_name(expression: ast.expr, binding: ast.alias, parent: ast.AST | None) -> str | None:
|
|
532
|
+
attributes: list[str] = []
|
|
533
|
+
while isinstance(expression, ast.Attribute):
|
|
534
|
+
attributes.append(expression.attr)
|
|
535
|
+
expression = expression.value
|
|
536
|
+
match parent:
|
|
537
|
+
case ast.Import():
|
|
538
|
+
prefix = binding.name if binding.asname else binding.name.partition(".")[0]
|
|
539
|
+
case ast.ImportFrom(module=str(module), level=0):
|
|
540
|
+
prefix = f"{module}.{binding.name}"
|
|
541
|
+
case _:
|
|
542
|
+
return None
|
|
543
|
+
return ".".join((prefix, *reversed(attributes)))
|
|
544
|
+
|
|
545
|
+
|
|
420
546
|
def _bound_names(node: ast.AST) -> tuple[str, ...]:
|
|
421
547
|
match node:
|
|
422
548
|
case ast.Name(id=name, ctx=ast.Store() | ast.Del()) | ast.arg(arg=name):
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
from collections import defaultdict
|
|
5
|
+
import symtable
|
|
6
|
+
from typing import TYPE_CHECKING, final
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from collections.abc import Mapping, Sequence
|
|
11
|
+
|
|
12
|
+
from sarj_python_lint._file_context import PythonFileContext
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@final
|
|
16
|
+
class RuntimeBindings:
|
|
17
|
+
def __init__(
|
|
18
|
+
self, context: PythonFileContext, bindings: Mapping[ast.AST | None, Mapping[str, Sequence[ast.AST]]]
|
|
19
|
+
) -> None:
|
|
20
|
+
self.bindings = bindings
|
|
21
|
+
self.tables: dict[int, symtable.SymbolTable] = {}
|
|
22
|
+
self.parents: dict[int, int | None] = {}
|
|
23
|
+
self.by_scope: dict[ast.AST, int] = {}
|
|
24
|
+
self.scopes: dict[int, ast.AST] = {}
|
|
25
|
+
self.mutable: set[tuple[int, str]] = set()
|
|
26
|
+
self.module: int | None = None
|
|
27
|
+
tree = context.tree
|
|
28
|
+
if tree is None:
|
|
29
|
+
return
|
|
30
|
+
try:
|
|
31
|
+
root = symtable.symtable(context.source, str(context.path), "exec")
|
|
32
|
+
except SyntaxError, UnicodeError, RecursionError:
|
|
33
|
+
return
|
|
34
|
+
self.module = root.get_id()
|
|
35
|
+
self._collect(root)
|
|
36
|
+
self._map_scopes(context, tree, root)
|
|
37
|
+
self._index_writes()
|
|
38
|
+
|
|
39
|
+
def lookup(self, scope: ast.AST | None, name: str) -> Sequence[ast.AST] | None:
|
|
40
|
+
if scope is None or (table_id := self.by_scope.get(scope)) is None:
|
|
41
|
+
return None
|
|
42
|
+
symbol = _symbol(self.tables[table_id], name)
|
|
43
|
+
if symbol is None or (owner := self._owner(table_id, symbol)) is None or (owner, name) in self.mutable:
|
|
44
|
+
return None
|
|
45
|
+
owner_scope = self.scopes.get(owner)
|
|
46
|
+
return self.bindings.get(owner_scope, {}).get(name) if owner_scope is not None else None
|
|
47
|
+
|
|
48
|
+
def _collect(self, root: symtable.SymbolTable) -> None:
|
|
49
|
+
pending: list[tuple[symtable.SymbolTable, int | None]] = [(root, None)]
|
|
50
|
+
while pending:
|
|
51
|
+
table, parent = pending.pop()
|
|
52
|
+
identity = table.get_id()
|
|
53
|
+
self.tables[identity] = table
|
|
54
|
+
self.parents[identity] = parent
|
|
55
|
+
pending.extend((child, identity) for child in table.get_children())
|
|
56
|
+
|
|
57
|
+
def _map_scopes(self, context: PythonFileContext, tree: ast.Module, root: symtable.SymbolTable) -> None:
|
|
58
|
+
candidates: dict[tuple[symtable.SymbolTableType, str, int], list[int]] = defaultdict(list)
|
|
59
|
+
for identity, table in self.tables.items():
|
|
60
|
+
candidates[table.get_type(), table.get_name(), table.get_lineno()].append(identity)
|
|
61
|
+
self.by_scope[tree] = root.get_id()
|
|
62
|
+
self.scopes[root.get_id()] = tree
|
|
63
|
+
for node in context.nodes(ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef):
|
|
64
|
+
kind = (
|
|
65
|
+
symtable.SymbolTableType.CLASS if isinstance(node, ast.ClassDef) else symtable.SymbolTableType.FUNCTION
|
|
66
|
+
)
|
|
67
|
+
matches = candidates.get((kind, node.name, node.lineno), ())
|
|
68
|
+
if len(matches) == 1:
|
|
69
|
+
[identity] = matches
|
|
70
|
+
self.by_scope[node] = identity
|
|
71
|
+
self.scopes[identity] = node
|
|
72
|
+
|
|
73
|
+
def _index_writes(self) -> None:
|
|
74
|
+
for identity, table in self.tables.items():
|
|
75
|
+
if identity == self.module:
|
|
76
|
+
continue
|
|
77
|
+
for symbol in table.get_symbols():
|
|
78
|
+
if not (symbol.is_global() or symbol.is_nonlocal()):
|
|
79
|
+
continue
|
|
80
|
+
if not (symbol.is_assigned() or symbol.is_imported() or symbol.is_namespace()):
|
|
81
|
+
continue
|
|
82
|
+
owner = self._owner(identity, symbol)
|
|
83
|
+
if owner is not None:
|
|
84
|
+
self.mutable.add((owner, symbol.get_name()))
|
|
85
|
+
|
|
86
|
+
def _owner(self, identity: int, symbol: symtable.Symbol) -> int | None:
|
|
87
|
+
if symbol.is_global():
|
|
88
|
+
return self.module
|
|
89
|
+
if symbol.is_local():
|
|
90
|
+
return identity
|
|
91
|
+
if not symbol.is_free():
|
|
92
|
+
return None
|
|
93
|
+
parent = self.parents.get(identity)
|
|
94
|
+
while parent is not None:
|
|
95
|
+
table = self.tables[parent]
|
|
96
|
+
if table.get_type() is symtable.SymbolTableType.MODULE:
|
|
97
|
+
break
|
|
98
|
+
if table.get_type() is symtable.SymbolTableType.CLASS and symbol.get_name() == "__class__":
|
|
99
|
+
# The compiler binds this cell to the lexically defined class.
|
|
100
|
+
return None
|
|
101
|
+
if table.get_type() is not symtable.SymbolTableType.CLASS:
|
|
102
|
+
ancestor = _symbol(table, symbol.get_name())
|
|
103
|
+
if ancestor is not None and ancestor.is_local():
|
|
104
|
+
return parent
|
|
105
|
+
parent = self.parents.get(parent)
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _symbol(table: symtable.SymbolTable, name: str) -> symtable.Symbol | None:
|
|
110
|
+
try:
|
|
111
|
+
return table.lookup(name)
|
|
112
|
+
except KeyError:
|
|
113
|
+
return None
|