sarj-python-lint 1.108.51__tar.gz → 1.108.52__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.108.51 → sarj_python_lint-1.108.52}/PKG-INFO +1 -1
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/pyproject.toml +1 -1
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_rule_descriptors.py +1 -1
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_binding_inventory.py +8 -2
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_closed_helpers.py +1 -1
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_declared_contracts.py +38 -3
- sarj_python_lint-1.108.52/src/sarj_python_lint/rules/_fixture_contracts.py +278 -0
- sarj_python_lint-1.108.52/src/sarj_python_lint/rules/_pytest_contract_parameters.py +539 -0
- sarj_python_lint-1.108.52/src/sarj_python_lint/rules/prefer_pytest_fixture_contract.py +156 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +2 -2
- sarj_python_lint-1.108.51/src/sarj_python_lint/rules/prefer_pytest_fixture_contract.py +0 -213
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/.gitignore +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/LICENSE +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/README.md +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_analysis_session.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_autofix.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_autofix_aliases.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_autofix_annotations.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_autofix_comments.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_autofix_declared_contracts.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_autofix_declared_tests.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_autofix_docs.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_autofix_enums.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_autofix_exhaustiveness.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_autofix_inert.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_autofix_parameters.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_autofix_repeated.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_autofix_security.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_autofix_sql_projection.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_autofix_tests.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_file_context.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_filesystem.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_fixes.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_parallel.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_prepared_sources.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_python_target.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_source.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/application_logging.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/autofix.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/benchmark.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/cast_access.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/interpreter_argv.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/json_boundary.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rule_metadata.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_annotation_origins.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_ast_names.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_ast_position.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_async_lifecycle.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_comment_narration.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_comments.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_copy_on_write.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_data_union_discriminator.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_docstrings.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_exports.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_fastapi.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_imports.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_inert_model_annotation.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_inherited_contract.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_local_alias_fix.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_local_source.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_loguru.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_mapping_shape_callbacks.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_namespace_access.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_native_namespace_capability.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_native_object_contracts.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_numeric_proof.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_opaque_contracts.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_postgres.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_postgres_ctas.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_project_index.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_pydantic_field_contract.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_registry.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_resolved_operation_signature.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_resource_provenance.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_route_shapes.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_runtime_bindings.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_runtime_names.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_runtime_registry.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_service_http_callbacks.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_set_fields.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_sql_aggregate_identity.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_sql_explain.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_sql_policy_source.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_static_json_alias.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_store_sql.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_store_writes.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_test_composition.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_test_provenance.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_transactional_sql.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/_type_guard_symbols.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/async_executor_shutdown.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/async_subprocess_cancellation_cleanup.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/cancel_owned_task_without_join.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/delete_route_returns_no_content.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/duplicate_adjacent_exception_handler.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/duplicate_closed_pure_helper.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/duplicate_pytest_fixture_params.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/failed_probe_zero_threshold.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/nan_hidden_by_builtin_min.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_async_client_in_constructor.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_async_function_cache.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_before_validator_input_mutation.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_blanket_suppression.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_broad_keyword_capture.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_conflicting_pydantic_alias_validation.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_contradictory_type_guard.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_create_verb_in_post_path.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_custom_control_flow_exception.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_defaulted_environment_setting.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_discarded_tagged_outcome.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_discarded_validation_result.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_dual_shape_model_field_reader.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_duplicate_field_validator_body.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_dynamic_builtins.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_exception_text_in_500_detail.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_fixed_sleep_in_loop.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_folded_bad_request_not_found.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_generic_json_parser.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_hasattr.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_impossible_pydantic_field_bounds.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_input_model_mutation.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_interpreter_source_arguments.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_invalid_pydantic_bypass.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_invalid_pydantic_validator_return.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_loguru_exc_info.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_loguru_printf_format.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_mocked_pydantic_value_object.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_mutating_container_transform.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_mutating_input_with_result.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_partial_type_is.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_recording_store.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_redundant_attribute_unpacking.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_redundant_local_alias.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_redundant_sql_alias.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_shallow_container_type_guard.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_shallow_record_type_guard.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_shared_mutable_default.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_slash_collection_root_under_prefix.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_sys_path_mutation.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_test_comments.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_test_method_grafting.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_unknown_pydantic_validator_field.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_unused_copy_result.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_unused_underscored_keyword_parameter.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/no_work_verb_get_route.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_application_model_joins.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_autospec_for_callable_mock.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_bulk_upsert.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_declarative_batch_validation.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_direct_import_module.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_discriminated_model_union.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_grouped_settings.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_native_string_check.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_optional_composite.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_precise_object_contract.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_psycopg_executemany.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_pydantic_model_assertion.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_pytest_dependency_fixtures.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_pytest_fixture_injection.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_required_constructor_parameters.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_runtime_validation.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_typed_cli.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_typed_mapping_get.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_typed_settings_field.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/pytest_fixture_returns_closed_resource.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/redundant_test_constructor_forwarder.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/repeated_unimplemented_test_contract.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/require_constrained_api_integer.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/require_exp_on_jwt_decode.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/require_injectable_retry_sleep.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/require_precise_factory_signature.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/require_pydantic_for_mapping_shape.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/service_singleton_delegates_to_bulk.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/store_field_update_reuses_upsert.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/store_insert_requires_explicit_conflict_policy.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/store_mutation_command_fanout.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/test_without_explicit_oracle.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/rules/unused_test_factory_option.py +0 -0
- {sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/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.108.
|
|
3
|
+
Version: 1.108.52
|
|
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/
|
{sarj_python_lint-1.108.51 → sarj_python_lint-1.108.52}/src/sarj_python_lint/_rule_descriptors.py
RENAMED
|
@@ -1507,7 +1507,7 @@ DESCRIPTORS: tuple[RuleDescriptor, ...] = (
|
|
|
1507
1507
|
module="sarj_python_lint.rules.prefer_pytest_fixture_contract",
|
|
1508
1508
|
class_name="PreferPytestFixtureContract",
|
|
1509
1509
|
code="SARJ550",
|
|
1510
|
-
description="Expose
|
|
1510
|
+
description="Expose existing abstract contracts from pytest fixtures and injected parameters.",
|
|
1511
1511
|
aliases=(),
|
|
1512
1512
|
enabled_by_default=True,
|
|
1513
1513
|
),
|
|
@@ -46,7 +46,7 @@ class BindingInventory:
|
|
|
46
46
|
for node in context.nodes(ast.AST):
|
|
47
47
|
if isinstance(node, ast.Name) and isinstance(node.ctx, ast.Load):
|
|
48
48
|
reads[node.id].append(node)
|
|
49
|
-
for name in
|
|
49
|
+
for name in binding_names(node):
|
|
50
50
|
bindings[_binding_scope(parents, node)][name].append(node)
|
|
51
51
|
wildcard |= name == "*"
|
|
52
52
|
if isinstance(node, ast.Global | ast.Nonlocal):
|
|
@@ -109,7 +109,7 @@ def _binding_scope(parents: Mapping[ast.AST, ast.AST], node: ast.AST) -> ast.AST
|
|
|
109
109
|
return scope
|
|
110
110
|
|
|
111
111
|
|
|
112
|
-
def
|
|
112
|
+
def binding_names(node: ast.AST) -> tuple[str, ...]:
|
|
113
113
|
match node:
|
|
114
114
|
case ast.Name(id=name, ctx=ast.Store() | ast.Del()) | ast.arg(arg=name):
|
|
115
115
|
return (name,)
|
|
@@ -129,3 +129,9 @@ def _bound_names(node: ast.AST) -> tuple[str, ...]:
|
|
|
129
129
|
return tuple(names)
|
|
130
130
|
case _:
|
|
131
131
|
return ()
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def scope_binding_names(statement: ast.AST) -> frozenset[str]:
|
|
135
|
+
nodes = tuple(ast.walk(statement))
|
|
136
|
+
parents = {child: node for node in nodes for child in ast.iter_child_nodes(node)}
|
|
137
|
+
return frozenset(name for node in nodes if _binding_scope(parents, node) is None for name in binding_names(node))
|
|
@@ -93,7 +93,7 @@ def direct_call(reference: ast.Name, context: PythonFileContext) -> ast.Call | N
|
|
|
93
93
|
return parent
|
|
94
94
|
|
|
95
95
|
|
|
96
|
-
def call_matches(function: ast.FunctionDef, call: ast.Call) -> bool:
|
|
96
|
+
def call_matches(function: ast.FunctionDef | ast.AsyncFunctionDef, call: ast.Call) -> bool:
|
|
97
97
|
positional = [*function.args.posonlyargs, *function.args.args]
|
|
98
98
|
keywords = {arg.arg for arg in (*function.args.args, *function.args.kwonlyargs)}
|
|
99
99
|
required = {arg.arg for arg in positional[: len(positional) - len(function.args.defaults)]}
|
|
@@ -5,6 +5,7 @@ from dataclasses import dataclass
|
|
|
5
5
|
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
7
|
from sarj_python_lint.rules._annotation_semantics import AnnotationSemantics
|
|
8
|
+
from sarj_python_lint.rules._binding_inventory import binding_names, scope_binding_names
|
|
8
9
|
from sarj_python_lint.rules._imports import ImportIndex
|
|
9
10
|
from sarj_python_lint.rules._project_index import ProjectIndexSet, SourceUnit, SymbolRef
|
|
10
11
|
|
|
@@ -21,6 +22,12 @@ _MAX_DEPTH = 8
|
|
|
21
22
|
_TYPING_SOURCES = frozenset({"typing", "typing_extensions"})
|
|
22
23
|
|
|
23
24
|
|
|
25
|
+
def _attribute_root(node: ast.expr) -> str | None:
|
|
26
|
+
while isinstance(node, ast.Attribute):
|
|
27
|
+
node = node.value
|
|
28
|
+
return node.id if isinstance(node, ast.Name) else None
|
|
29
|
+
|
|
30
|
+
|
|
24
31
|
@dataclass(frozen=True)
|
|
25
32
|
class DeclaredClass:
|
|
26
33
|
unit: SourceUnit
|
|
@@ -53,6 +60,7 @@ class DeclaredContracts:
|
|
|
53
60
|
self.context = context
|
|
54
61
|
self._field_cache: dict[tuple[ast.ClassDef, bool], dict[str, ast.AnnAssign]] = {}
|
|
55
62
|
self._module_annotations: dict[Path, AnnotationSemantics] = {}
|
|
63
|
+
self._scope_names: dict[ast.AST, frozenset[str]] = {}
|
|
56
64
|
self.project = context.session.project or ProjectIndexSet.single(context.path, context.source)
|
|
57
65
|
self.unit = self.project.unit(context.path)
|
|
58
66
|
if self.unit is not None and self.unit.tree is not context.tree:
|
|
@@ -85,29 +93,48 @@ class DeclaredContracts:
|
|
|
85
93
|
return DeclaredClass(source, declaration) if declaration is not None else None
|
|
86
94
|
|
|
87
95
|
def _lookup_allowed(self, expression: ast.expr, owner: SourceUnit, at: ast.AST | None) -> bool:
|
|
88
|
-
if owner is not self.unit or (at or expression) not in self.context.parents:
|
|
89
|
-
return True
|
|
90
96
|
root = expression
|
|
91
97
|
while isinstance(root, ast.Attribute):
|
|
92
98
|
root = root.value
|
|
93
99
|
if not isinstance(root, ast.Name):
|
|
94
100
|
return True
|
|
101
|
+
if owner is not self.unit:
|
|
102
|
+
return self._foreign_lookup_allowed(owner, root.id)
|
|
103
|
+
if (at or expression) not in self.context.parents:
|
|
104
|
+
return True
|
|
95
105
|
binding = self.context.static_resource_provenance.binding(root.id, at or expression)
|
|
96
106
|
return (
|
|
97
107
|
isinstance(binding, ast.alias | ast.ClassDef)
|
|
98
108
|
and root.id not in self.context.static_resource_provenance.mutated
|
|
99
109
|
)
|
|
100
110
|
|
|
111
|
+
def _foreign_lookup_allowed(self, owner: SourceUnit, name: str) -> bool:
|
|
112
|
+
if owner.tree is None:
|
|
113
|
+
return False
|
|
114
|
+
if name not in owner.imports:
|
|
115
|
+
return True
|
|
116
|
+
imports = self._module_semantics(owner).imports
|
|
117
|
+
if name in imports.shadowed_names:
|
|
118
|
+
return False
|
|
119
|
+
return not any(
|
|
120
|
+
isinstance(node, ast.Attribute) and not isinstance(node.ctx, ast.Load) and _attribute_root(node) == name
|
|
121
|
+
for node in ast.walk(owner.tree)
|
|
122
|
+
)
|
|
123
|
+
|
|
101
124
|
def _stable_declaration(self, source: SourceUnit, name: str) -> ast.ClassDef | None:
|
|
102
125
|
if source.tree is None:
|
|
103
126
|
return None
|
|
104
127
|
declarations = [node for node in source.tree.body if isinstance(node, ast.ClassDef) and node.name == name]
|
|
105
128
|
if len(declarations) != 1 or declarations[0].keywords:
|
|
106
129
|
return None
|
|
130
|
+
if any(
|
|
131
|
+
isinstance(node, ast.FunctionDef | ast.AsyncFunctionDef) and node.name == name for node in source.tree.body
|
|
132
|
+
):
|
|
133
|
+
return None
|
|
107
134
|
if not self._identity_decorators(source, declarations[0]):
|
|
108
135
|
return None
|
|
109
136
|
if any(
|
|
110
|
-
|
|
137
|
+
name in binding_names(node)
|
|
111
138
|
for statement in source.tree.body
|
|
112
139
|
if not isinstance(statement, ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef)
|
|
113
140
|
for node in ast.walk(statement)
|
|
@@ -175,6 +202,14 @@ class DeclaredContracts:
|
|
|
175
202
|
self._module_annotations[unit.path] = semantics
|
|
176
203
|
return semantics
|
|
177
204
|
|
|
205
|
+
def imports_for(self, unit: SourceUnit) -> ImportIndex:
|
|
206
|
+
return self._module_semantics(unit).imports
|
|
207
|
+
|
|
208
|
+
def bound_names(self, statement: ast.AST) -> frozenset[str]:
|
|
209
|
+
if statement not in self._scope_names:
|
|
210
|
+
self._scope_names[statement] = scope_binding_names(statement)
|
|
211
|
+
return self._scope_names[statement]
|
|
212
|
+
|
|
178
213
|
def annotation(self, expression: ast.expr, unit: SourceUnit | None = None) -> DeclaredClass | None:
|
|
179
214
|
semantics = self.context.annotation_semantics
|
|
180
215
|
if unit is not None and unit is not self.unit and unit.tree is not None:
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from sarj_python_lint.rules._binding_inventory import scope_binding_names
|
|
8
|
+
from sarj_python_lint.rules._declared_contracts import DeclaredClass
|
|
9
|
+
from sarj_python_lint.rules._project_index import SourceUnit, SymbolRef
|
|
10
|
+
from sarj_python_lint.rules._resolved_operation_signature import resolved_operation_signature
|
|
11
|
+
from sarj_python_lint.rules.pytest_fixture_returns_bare_tuple import fixture_yields
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from sarj_python_lint.rules._declared_contracts import DeclaredContracts
|
|
16
|
+
from sarj_python_lint.rules._imports import ImportIndex
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
PORT_BASES = frozenset({"abc.ABC", "typing.Protocol", "typing_extensions.Protocol"})
|
|
20
|
+
MAX_DEPTH = 8
|
|
21
|
+
_METHOD_DECORATORS = frozenset({"abc.abstractmethod", "typing.override", "typing_extensions.override"})
|
|
22
|
+
CONSTRUCTION_HOOKS = frozenset({"__new__", "__init_subclass__"})
|
|
23
|
+
_MEMBER_HOOKS = frozenset({"__getattribute__", "__getattr__", "__init_subclass__"})
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
|
+
class DeclaredMethod:
|
|
28
|
+
declaration: ast.FunctionDef | ast.AsyncFunctionDef
|
|
29
|
+
owner: DeclaredClass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def abstract_contract(model: DeclaredClass, contracts: DeclaredContracts) -> bool:
|
|
33
|
+
if model.unit.tree is None:
|
|
34
|
+
return False
|
|
35
|
+
imports = contracts.imports_for(model.unit)
|
|
36
|
+
return any(
|
|
37
|
+
imports.resolved_qualified_name(base) in {"typing.Protocol", "typing_extensions.Protocol"}
|
|
38
|
+
for base in model.declaration.bases
|
|
39
|
+
) or bool(_abstract_members(model, contracts))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _abstract_members(
|
|
43
|
+
model: DeclaredClass,
|
|
44
|
+
contracts: DeclaredContracts,
|
|
45
|
+
seen: frozenset[ast.ClassDef] = frozenset(),
|
|
46
|
+
*,
|
|
47
|
+
cache: dict[tuple[ast.ClassDef, int], frozenset[str]] | None = None,
|
|
48
|
+
) -> frozenset[str]:
|
|
49
|
+
if model.unit.tree is None or model.declaration in seen or len(seen) >= MAX_DEPTH:
|
|
50
|
+
return frozenset()
|
|
51
|
+
memo = {} if cache is None else cache
|
|
52
|
+
key = model.declaration, len(seen)
|
|
53
|
+
if key in memo:
|
|
54
|
+
return memo[key]
|
|
55
|
+
imports = contracts.imports_for(model.unit)
|
|
56
|
+
pending: set[str] = set()
|
|
57
|
+
for base in model.declaration.bases:
|
|
58
|
+
parent = contracts.class_type(base, model.unit)
|
|
59
|
+
if parent is not None:
|
|
60
|
+
pending.update(_abstract_members(parent, contracts, seen | {model.declaration}, cache=memo))
|
|
61
|
+
for member in model.declaration.body:
|
|
62
|
+
if isinstance(member, ast.FunctionDef | ast.AsyncFunctionDef):
|
|
63
|
+
if any(
|
|
64
|
+
imports.resolves(decorator, sources=frozenset({"abc"}), symbol="abstractmethod")
|
|
65
|
+
for decorator in member.decorator_list
|
|
66
|
+
):
|
|
67
|
+
pending.add(member.name)
|
|
68
|
+
else:
|
|
69
|
+
pending.discard(member.name)
|
|
70
|
+
result = frozenset(pending)
|
|
71
|
+
memo[key] = result
|
|
72
|
+
return result
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def abstract_ancestors(model: DeclaredClass, contracts: DeclaredContracts) -> tuple[DeclaredClass, ...]:
|
|
76
|
+
pending = [(model, 0)]
|
|
77
|
+
seen: set[ast.ClassDef] = set()
|
|
78
|
+
found: dict[ast.ClassDef, DeclaredClass] = {}
|
|
79
|
+
checked: list[DeclaredClass] = []
|
|
80
|
+
while pending:
|
|
81
|
+
current, depth = pending.pop()
|
|
82
|
+
if depth >= MAX_DEPTH:
|
|
83
|
+
return ()
|
|
84
|
+
if current.declaration in seen:
|
|
85
|
+
continue
|
|
86
|
+
seen.add(current.declaration)
|
|
87
|
+
checked.append(current)
|
|
88
|
+
for base in current.declaration.bases:
|
|
89
|
+
owner = contracts.class_type(base, current.unit)
|
|
90
|
+
if owner is None:
|
|
91
|
+
continue
|
|
92
|
+
if abstract_contract(owner, contracts) and contracts.inherits(owner, PORT_BASES):
|
|
93
|
+
found[owner.declaration] = owner
|
|
94
|
+
else:
|
|
95
|
+
pending.append((owner, depth + 1))
|
|
96
|
+
candidates = tuple(found.values())
|
|
97
|
+
if len(candidates) != 1:
|
|
98
|
+
return candidates
|
|
99
|
+
return candidates if all(class_identity_stable(current, contracts) for current in checked) else ()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def contract_accepts(model: DeclaredClass, target: DeclaredClass, contracts: DeclaredContracts) -> bool:
|
|
103
|
+
pending = [(model, 0)]
|
|
104
|
+
seen: set[ast.ClassDef] = set()
|
|
105
|
+
while pending:
|
|
106
|
+
current, depth = pending.pop()
|
|
107
|
+
if current.declaration is target.declaration:
|
|
108
|
+
return True
|
|
109
|
+
if current.declaration in seen or depth >= MAX_DEPTH:
|
|
110
|
+
continue
|
|
111
|
+
seen.add(current.declaration)
|
|
112
|
+
pending.extend(
|
|
113
|
+
(parent, depth + 1)
|
|
114
|
+
for base in current.declaration.bases
|
|
115
|
+
if (parent := contracts.class_type(base, current.unit)) is not None
|
|
116
|
+
)
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def has_class_hook(
|
|
121
|
+
model: DeclaredClass,
|
|
122
|
+
contracts: DeclaredContracts,
|
|
123
|
+
names: frozenset[str],
|
|
124
|
+
) -> bool:
|
|
125
|
+
pending = [(model, 0)]
|
|
126
|
+
seen: set[ast.ClassDef] = set()
|
|
127
|
+
while pending:
|
|
128
|
+
current, depth = pending.pop()
|
|
129
|
+
if depth >= MAX_DEPTH:
|
|
130
|
+
return True
|
|
131
|
+
if current.declaration in seen:
|
|
132
|
+
continue
|
|
133
|
+
seen.add(current.declaration)
|
|
134
|
+
if _declares_hook(current, names):
|
|
135
|
+
return True
|
|
136
|
+
parents = _hook_parents(current, contracts)
|
|
137
|
+
if parents is None:
|
|
138
|
+
return True
|
|
139
|
+
pending.extend((parent, depth + 1) for parent in parents)
|
|
140
|
+
return False
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _declares_hook(model: DeclaredClass, names: frozenset[str]) -> bool:
|
|
144
|
+
return any(
|
|
145
|
+
(isinstance(member, ast.FunctionDef | ast.AsyncFunctionDef) and member.name in names)
|
|
146
|
+
or any(_class_member_replaced(member, name) for name in names)
|
|
147
|
+
for member in model.declaration.body
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _hook_parents(model: DeclaredClass, contracts: DeclaredContracts) -> tuple[DeclaredClass, ...] | None:
|
|
152
|
+
imports = contracts.imports_for(model.unit) if model.unit.tree is not None else None
|
|
153
|
+
parents: list[DeclaredClass] = []
|
|
154
|
+
for base in model.declaration.bases:
|
|
155
|
+
parent = contracts.class_type(base, model.unit)
|
|
156
|
+
if parent is not None:
|
|
157
|
+
parents.append(parent)
|
|
158
|
+
elif imports is None or imports.resolved_qualified_name(base) not in PORT_BASES:
|
|
159
|
+
return None
|
|
160
|
+
return tuple(parents)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def method_definition(
|
|
164
|
+
model: DeclaredClass,
|
|
165
|
+
name: str,
|
|
166
|
+
contracts: DeclaredContracts,
|
|
167
|
+
seen: frozenset[ast.ClassDef] = frozenset(),
|
|
168
|
+
) -> DeclaredMethod | None:
|
|
169
|
+
if (
|
|
170
|
+
model.declaration in seen
|
|
171
|
+
or len(seen) >= MAX_DEPTH
|
|
172
|
+
or model.declaration.type_params
|
|
173
|
+
or not class_identity_stable(model, contracts)
|
|
174
|
+
):
|
|
175
|
+
return None
|
|
176
|
+
if _method_replaced(model.declaration, name):
|
|
177
|
+
return None
|
|
178
|
+
definitions = [
|
|
179
|
+
member
|
|
180
|
+
for member in model.declaration.body
|
|
181
|
+
if isinstance(member, ast.FunctionDef | ast.AsyncFunctionDef) and member.name == name
|
|
182
|
+
]
|
|
183
|
+
if definitions:
|
|
184
|
+
return DeclaredMethod(definitions[0], model) if len(definitions) == 1 else None
|
|
185
|
+
if len(model.declaration.bases) != 1:
|
|
186
|
+
return None
|
|
187
|
+
inherited = [
|
|
188
|
+
result
|
|
189
|
+
for base in model.declaration.bases
|
|
190
|
+
if (parent := contracts.class_type(base, model.unit)) is not None
|
|
191
|
+
and (result := method_definition(parent, name, contracts, seen | {model.declaration})) is not None
|
|
192
|
+
]
|
|
193
|
+
return inherited[0] if len(inherited) == 1 else None
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _method_replaced(declaration: ast.ClassDef, name: str) -> bool:
|
|
197
|
+
instance_write = any(
|
|
198
|
+
isinstance(node, ast.Attribute) and node.attr == name and isinstance(node.ctx, ast.Store | ast.Del)
|
|
199
|
+
for node in ast.walk(declaration)
|
|
200
|
+
)
|
|
201
|
+
return instance_write or any(_class_member_replaced(member, name) for member in declaration.body)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _class_member_replaced(member: ast.AST, name: str) -> bool:
|
|
205
|
+
match member:
|
|
206
|
+
case ast.FunctionDef() | ast.AsyncFunctionDef():
|
|
207
|
+
return False
|
|
208
|
+
case ast.ClassDef(name=bound):
|
|
209
|
+
return bound == name
|
|
210
|
+
case _:
|
|
211
|
+
pass
|
|
212
|
+
return name in scope_binding_names(member)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def compatible_operation(
|
|
216
|
+
implementation: DeclaredClass, contract: DeclaredClass, name: str, contracts: DeclaredContracts
|
|
217
|
+
) -> bool:
|
|
218
|
+
if name.startswith("_") or has_class_hook(implementation, contracts, _MEMBER_HOOKS):
|
|
219
|
+
return False
|
|
220
|
+
concrete = method_definition(implementation, name, contracts)
|
|
221
|
+
abstract = method_definition(contract, name, contracts)
|
|
222
|
+
if concrete is None or abstract is None:
|
|
223
|
+
return False
|
|
224
|
+
if isinstance(concrete.declaration, ast.AsyncFunctionDef) and fixture_yields(
|
|
225
|
+
concrete.declaration
|
|
226
|
+
) != fixture_yields(abstract.declaration):
|
|
227
|
+
return False
|
|
228
|
+
concrete_signature = _operation_signature(concrete, contracts)
|
|
229
|
+
return concrete_signature is not None and concrete_signature == _operation_signature(abstract, contracts)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def class_identity_stable(model: DeclaredClass, contracts: DeclaredContracts) -> bool:
|
|
233
|
+
module = model.unit.module
|
|
234
|
+
return module is not None and not contracts.project.nominal_membership_is_dynamic(
|
|
235
|
+
SymbolRef(module, model.declaration.name)
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _operation_signature(definition: DeclaredMethod, contracts: DeclaredContracts) -> str | None:
|
|
240
|
+
method, unit = definition.declaration, definition.owner.unit
|
|
241
|
+
if unit.tree is None:
|
|
242
|
+
return None
|
|
243
|
+
imports = contracts.imports_for(unit)
|
|
244
|
+
if any(imports.resolved_qualified_name(decorator) not in _METHOD_DECORATORS for decorator in method.decorator_list):
|
|
245
|
+
return None
|
|
246
|
+
annotations = [
|
|
247
|
+
method.returns,
|
|
248
|
+
*(argument.annotation for argument in (*method.args.posonlyargs, *method.args.args, *method.args.kwonlyargs)),
|
|
249
|
+
]
|
|
250
|
+
shadowed = frozenset(name for member in definition.owner.declaration.body for name in contracts.bound_names(member))
|
|
251
|
+
if any(
|
|
252
|
+
_opaque_annotation(annotation, unit, contracts, imports, shadowed)
|
|
253
|
+
for annotation in annotations
|
|
254
|
+
if annotation is not None
|
|
255
|
+
):
|
|
256
|
+
return None
|
|
257
|
+
return resolved_operation_signature(method, unit, imports)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _opaque_annotation(
|
|
261
|
+
annotation: ast.expr, unit: SourceUnit, contracts: DeclaredContracts, imports: ImportIndex, shadowed: frozenset[str]
|
|
262
|
+
) -> bool:
|
|
263
|
+
for node in ast.walk(annotation):
|
|
264
|
+
if not isinstance(node, ast.Name | ast.Attribute):
|
|
265
|
+
continue
|
|
266
|
+
if isinstance(node, ast.Name) and node.id in shadowed:
|
|
267
|
+
return True
|
|
268
|
+
if imports.resolved_qualified_name(node) in {"typing.Self", "typing_extensions.Self"}:
|
|
269
|
+
return True
|
|
270
|
+
if _source_alias_annotation(node, unit, contracts):
|
|
271
|
+
return True
|
|
272
|
+
return False
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _source_alias_annotation(node: ast.Name | ast.Attribute, unit: SourceUnit, contracts: DeclaredContracts) -> bool:
|
|
276
|
+
symbol = contracts.project.resolve(unit, node)
|
|
277
|
+
source = contracts.project.source_unit(symbol.module) if symbol is not None else None
|
|
278
|
+
return source is not None and source is not unit and contracts.class_type(node, unit) is None
|