sarj-python-lint 1.95.0__tar.gz → 1.99.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.95.0 → sarj_python_lint-1.99.0}/PKG-INFO +1 -1
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/pyproject.toml +1 -1
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/__main__.py +1 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_rule_descriptors.py +9 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_native_object_contracts.py +9 -7
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_project_index.py +132 -1
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_registry.py +2 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +17 -5
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_custom_control_flow_exception.py +25 -11
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_discarded_tagged_outcome.py +50 -1
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +90 -29
- sarj_python_lint-1.99.0/src/sarj_python_lint/rules/no_recording_store.py +491 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_redundant_sql_alias.py +8 -1
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_application_model_joins.py +26 -13
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +150 -76
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_optional_composite.py +47 -21
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_precise_object_contract.py +9 -40
- sarj_python_lint-1.99.0/src/sarj_python_lint/rules/service_singleton_delegates_to_bulk.py +515 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/store_insert_requires_explicit_conflict_policy.py +37 -11
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/store_mutation_command_fanout.py +84 -16
- sarj_python_lint-1.95.0/src/sarj_python_lint/rules/no_recording_store.py +0 -276
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/.gitignore +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/LICENSE +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/README.md +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_analysis_session.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_autofix.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_autofix_aliases.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_autofix_annotations.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_autofix_comments.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_autofix_declared_contracts.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_autofix_declared_tests.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_autofix_docs.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_autofix_enums.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_autofix_exhaustiveness.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_autofix_inert.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_autofix_parameters.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_autofix_repeated.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_autofix_security.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_autofix_sql_projection.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_autofix_tests.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_file_context.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_filesystem.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_fixes.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_parallel.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_prepared_sources.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_python_target.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_source.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/application_logging.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/autofix.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/benchmark.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/cast_access.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/interpreter_argv.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/json_boundary.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rule_base.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rule_metadata.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_annotation_origins.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_annotation_semantics.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_ast_names.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_ast_position.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_async_lifecycle.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_binding_inventory.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_closed_helpers.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_comment_narration.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_comments.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_copy_on_write.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_data_union_discriminator.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_declared_contracts.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_docstrings.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_exports.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_fastapi.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_imports.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_inert_model_annotation.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_inherited_contract.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_local_alias_fix.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_local_source.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_mapping_shape_callbacks.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_namespace_access.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_native_namespace_capability.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_numeric_proof.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_opaque_contracts.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_paths.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_postgres.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_pydantic_field_contract.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_resolved_operation_signature.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_resource_provenance.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_route_shapes.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_runtime_bindings.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_runtime_registry.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_service_http_callbacks.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_set_fields.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_sql_aggregate_identity.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_sql_explain.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_sql_policy_source.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_static_json_alias.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_store_sql.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_store_writes.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_test_composition.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_test_provenance.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_transactional_sql.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_type_guard_symbols.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/async_executor_shutdown.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/async_subprocess_cancellation_cleanup.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/cancel_owned_task_without_join.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/delete_route_returns_no_content.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/duplicate_adjacent_exception_handler.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/duplicate_closed_pure_helper.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/duplicate_pytest_fixture_params.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/failed_probe_zero_threshold.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/nan_hidden_by_builtin_min.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_async_client_in_constructor.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_async_function_cache.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_before_validator_input_mutation.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_blanket_suppression.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_broad_keyword_capture.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_conflicting_pydantic_alias_validation.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_contradictory_type_guard.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_create_verb_in_post_path.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_defaulted_environment_setting.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_discarded_validation_result.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_dual_shape_model_field_reader.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_duplicate_field_validator_body.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_exception_text_in_500_detail.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_fixed_sleep_in_loop.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_folded_bad_request_not_found.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_generic_json_parser.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_hasattr.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_impossible_pydantic_field_bounds.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_input_model_mutation.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_interpreter_source_arguments.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_invalid_pydantic_bypass.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_invalid_pydantic_validator_return.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_loguru_exc_info.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_loguru_printf_format.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_mocked_pydantic_value_object.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_mutating_container_transform.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_mutating_input_with_result.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_partial_type_is.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_redundant_attribute_unpacking.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_redundant_local_alias.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_shallow_container_type_guard.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_shallow_record_type_guard.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_slash_collection_root_under_prefix.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_test_comments.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_test_method_grafting.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_unknown_pydantic_validator_field.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_unused_copy_result.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_unused_underscored_keyword_parameter.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/no_work_verb_get_route.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_autospec_for_callable_mock.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_bulk_upsert.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_declarative_batch_validation.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_direct_import_module.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_discriminated_model_union.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_grouped_settings.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_native_string_check.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_psycopg_executemany.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_pydantic_model_assertion.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_pytest_dependency_fixtures.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_pytest_fixture_contract.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_pytest_fixture_injection.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_required_constructor_parameters.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_runtime_validation.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_typed_cli.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_typed_mapping_get.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_typed_settings_field.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/pytest_fixture_returns_closed_resource.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/redundant_test_constructor_forwarder.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/repeated_unimplemented_test_contract.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/require_constrained_api_integer.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/require_exp_on_jwt_decode.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/require_injectable_retry_sleep.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/require_precise_factory_signature.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/require_pydantic_for_mapping_shape.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/store_field_update_reuses_upsert.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/test_without_explicit_oracle.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/unused_test_factory_option.py +0 -0
- {sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/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.
|
|
3
|
+
Version: 1.99.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/
|
{sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/_rule_descriptors.py
RENAMED
|
@@ -8,6 +8,15 @@ from sarj_python_lint.rule_metadata import RuleDescriptor
|
|
|
8
8
|
|
|
9
9
|
SCHEMA_VERSION = 1
|
|
10
10
|
DESCRIPTORS: tuple[RuleDescriptor, ...] = (
|
|
11
|
+
RuleDescriptor(
|
|
12
|
+
rule_id=RuleId("service-singleton-delegates-to-bulk"),
|
|
13
|
+
module="sarj_python_lint.rules.service_singleton_delegates_to_bulk",
|
|
14
|
+
class_name="ServiceSingletonDelegatesToBulk",
|
|
15
|
+
code="SARJ556",
|
|
16
|
+
description="Service singleton CRUD operations must delegate to a bulk primitive.",
|
|
17
|
+
aliases=(),
|
|
18
|
+
enabled_by_default=True,
|
|
19
|
+
),
|
|
11
20
|
RuleDescriptor(
|
|
12
21
|
rule_id=RuleId("no-async-client-in-constructor"),
|
|
13
22
|
module="sarj_python_lint.rules.no_async_client_in_constructor",
|
|
@@ -701,9 +701,13 @@ def _stub(statement: ast.stmt) -> bool:
|
|
|
701
701
|
|
|
702
702
|
|
|
703
703
|
def builtin_object(node: ast.expr, facts: MappingShapeFacts, at: ast.AST) -> bool:
|
|
704
|
-
|
|
704
|
+
return builtin_symbol(node, "object", facts, at)
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
def builtin_symbol(node: ast.expr, name: str, facts: MappingShapeFacts, at: ast.AST) -> bool:
|
|
708
|
+
candidate = facts.qualified(node, at) == f"builtins.{name}" or (
|
|
705
709
|
isinstance(node, ast.Name)
|
|
706
|
-
and node.id ==
|
|
710
|
+
and node.id == name
|
|
707
711
|
and not facts.shadowed(node.id, at)
|
|
708
712
|
and facts.provenance.binding(node.id, at) is None
|
|
709
713
|
)
|
|
@@ -717,13 +721,11 @@ def builtin_object(node: ast.expr, facts: MappingShapeFacts, at: ast.AST) -> boo
|
|
|
717
721
|
while isinstance(root, ast.Attribute):
|
|
718
722
|
root = root.value
|
|
719
723
|
if isinstance(root, ast.Name):
|
|
720
|
-
changed = native.namespace_replaced(root.id) if root.id ==
|
|
724
|
+
changed = native.namespace_replaced(root.id) if root.id == name else native.namespace_rebound(root.id)
|
|
721
725
|
if changed:
|
|
722
726
|
return False
|
|
723
727
|
attribute_mutation = any(
|
|
724
|
-
isinstance(member.ctx, ast.Store | ast.Del)
|
|
725
|
-
and member.attr == "object"
|
|
726
|
-
and _builtins_module(member.value, context)
|
|
728
|
+
isinstance(member.ctx, ast.Store | ast.Del) and member.attr == name and _builtins_module(member.value, context)
|
|
727
729
|
for member in context.nodes(ast.Attribute)
|
|
728
730
|
)
|
|
729
731
|
return not attribute_mutation and not any(
|
|
@@ -731,7 +733,7 @@ def builtin_object(node: ast.expr, facts: MappingShapeFacts, at: ast.AST) -> boo
|
|
|
731
733
|
and call.func.id in {"setattr", "delattr"}
|
|
732
734
|
and len(call.args) >= _PAIR_ARITY
|
|
733
735
|
and _builtins_module(call.args[0], context)
|
|
734
|
-
and (not isinstance(call.args[1], ast.Constant) or call.args[1].value ==
|
|
736
|
+
and (not isinstance(call.args[1], ast.Constant) or call.args[1].value == name)
|
|
735
737
|
for call in context.nodes(ast.Call)
|
|
736
738
|
)
|
|
737
739
|
|
{sarj_python_lint-1.95.0 → sarj_python_lint-1.99.0}/src/sarj_python_lint/rules/_project_index.py
RENAMED
|
@@ -12,6 +12,7 @@ from pathlib import Path
|
|
|
12
12
|
import re
|
|
13
13
|
from types import MappingProxyType
|
|
14
14
|
from typing import TYPE_CHECKING, final
|
|
15
|
+
from weakref import WeakMethod
|
|
15
16
|
|
|
16
17
|
from sarj_rule_contracts import SourceSnapshot
|
|
17
18
|
|
|
@@ -25,7 +26,7 @@ from sarj_python_lint.rules._paths import is_generated
|
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
if TYPE_CHECKING:
|
|
28
|
-
from collections.abc import Iterator, Sequence, Set as AbstractSet
|
|
29
|
+
from collections.abc import Callable, Iterator, Sequence, Set as AbstractSet
|
|
29
30
|
from typing import Self
|
|
30
31
|
|
|
31
32
|
from sarj_python_lint._prepared_sources import PreparedSourceInputs
|
|
@@ -191,6 +192,13 @@ class _TypeProviderFacts:
|
|
|
191
192
|
opaque: bool
|
|
192
193
|
|
|
193
194
|
|
|
195
|
+
@dataclass(frozen=True)
|
|
196
|
+
class _ImportNamespaceWrites:
|
|
197
|
+
modules: frozenset[str]
|
|
198
|
+
dependencies: frozenset[str]
|
|
199
|
+
incomplete: bool
|
|
200
|
+
|
|
201
|
+
|
|
194
202
|
def imports_outside(tree: ast.Module | None, modules: frozenset[str]) -> bool:
|
|
195
203
|
if tree is None:
|
|
196
204
|
return True
|
|
@@ -202,6 +210,48 @@ def imports_outside(tree: ast.Module | None, modules: frozenset[str]) -> bool:
|
|
|
202
210
|
return False
|
|
203
211
|
|
|
204
212
|
|
|
213
|
+
@final
|
|
214
|
+
class ImportNamespaceMutations:
|
|
215
|
+
def __init__(
|
|
216
|
+
self,
|
|
217
|
+
load: Callable[[], Callable[[], Mapping[str, _ImportNamespaceWrites]] | None],
|
|
218
|
+
) -> None:
|
|
219
|
+
self._load = load
|
|
220
|
+
self._facts: Mapping[str, _ImportNamespaceWrites] | None = None
|
|
221
|
+
self._reachable: dict[str, frozenset[str] | None] = {}
|
|
222
|
+
|
|
223
|
+
def for_unit(self, unit: SourceUnit) -> frozenset[str] | None:
|
|
224
|
+
if unit.module is None:
|
|
225
|
+
return frozenset()
|
|
226
|
+
if unit.module in self._reachable:
|
|
227
|
+
return self._reachable[unit.module]
|
|
228
|
+
pending = list(_module_prefixes(unit.module))
|
|
229
|
+
visited: set[str] = set()
|
|
230
|
+
mutated: set[str] = set()
|
|
231
|
+
if self._facts is None:
|
|
232
|
+
load = self._load()
|
|
233
|
+
if load is None:
|
|
234
|
+
return None
|
|
235
|
+
self._facts = load()
|
|
236
|
+
facts = self._facts
|
|
237
|
+
while pending:
|
|
238
|
+
module = pending.pop()
|
|
239
|
+
if module in visited:
|
|
240
|
+
continue
|
|
241
|
+
visited.add(module)
|
|
242
|
+
fact = facts.get(module)
|
|
243
|
+
if fact is None:
|
|
244
|
+
continue
|
|
245
|
+
if fact.incomplete:
|
|
246
|
+
self._reachable[unit.module] = None
|
|
247
|
+
return None
|
|
248
|
+
mutated.update(fact.modules)
|
|
249
|
+
pending.extend(fact.dependencies - visited)
|
|
250
|
+
result = frozenset(mutated)
|
|
251
|
+
self._reachable[unit.module] = result
|
|
252
|
+
return result
|
|
253
|
+
|
|
254
|
+
|
|
205
255
|
@final
|
|
206
256
|
class ProjectIndexSet:
|
|
207
257
|
# Borrowed ASTs and import mappings must stay unchanged for this index's lifetime.
|
|
@@ -225,6 +275,7 @@ class ProjectIndexSet:
|
|
|
225
275
|
self._type_providers: dict[ast.Module, _TypeProviderFacts] = {}
|
|
226
276
|
self._bound_value_prefixes: dict[str, bool] = {}
|
|
227
277
|
self._value_origins_incomplete = False
|
|
278
|
+
self.import_namespace_mutations = ImportNamespaceMutations(WeakMethod(self._namespace_write_inventory))
|
|
228
279
|
by_module = {unit.module: unit for unit in units.values() if unit.module is not None}
|
|
229
280
|
self._by_module = MappingProxyType(by_module)
|
|
230
281
|
classes: dict[SymbolRef, ClassSummary] = {}
|
|
@@ -380,6 +431,13 @@ class ProjectIndexSet:
|
|
|
380
431
|
def source_unit(self, module: str) -> SourceUnit | None:
|
|
381
432
|
return self._by_module.get(module)
|
|
382
433
|
|
|
434
|
+
def _namespace_write_inventory(self) -> Mapping[str, _ImportNamespaceWrites]:
|
|
435
|
+
def resolve(origin: SymbolRef) -> tuple[SymbolRef, ...] | None:
|
|
436
|
+
values = self._canonical_value_symbols(origin)
|
|
437
|
+
return None if not values and self._value_origins_incomplete else values
|
|
438
|
+
|
|
439
|
+
return _import_namespace_write_facts(self._units, self._runtime_origins, resolve)
|
|
440
|
+
|
|
383
441
|
def nominal_membership_is_dynamic(self, symbol: SymbolRef) -> bool:
|
|
384
442
|
if symbol.module not in self._namespace_mutation:
|
|
385
443
|
unit = self.source_unit(symbol.module)
|
|
@@ -767,6 +825,79 @@ def _namespace_closure(
|
|
|
767
825
|
return MappingProxyType({name: frozenset(targets) for name, targets in values.items()})
|
|
768
826
|
|
|
769
827
|
|
|
828
|
+
def _import_namespace_write_facts(
|
|
829
|
+
units: Mapping[Path, SourceUnit],
|
|
830
|
+
runtime_origins: Mapping[str, Mapping[str, frozenset[SymbolRef]]],
|
|
831
|
+
resolve: Callable[[SymbolRef], tuple[SymbolRef, ...] | None],
|
|
832
|
+
) -> Mapping[str, _ImportNamespaceWrites]:
|
|
833
|
+
facts: dict[str, _ImportNamespaceWrites] = {}
|
|
834
|
+
for unit in units.values():
|
|
835
|
+
if unit.module is None or unit.tree is None:
|
|
836
|
+
continue
|
|
837
|
+
current = _unit_import_namespace_writes(unit, runtime_origins.get(unit.module, {}), resolve)
|
|
838
|
+
previous = facts.get(unit.module)
|
|
839
|
+
if previous is not None:
|
|
840
|
+
current = _ImportNamespaceWrites(
|
|
841
|
+
previous.modules | current.modules,
|
|
842
|
+
previous.dependencies | current.dependencies,
|
|
843
|
+
previous.incomplete or current.incomplete,
|
|
844
|
+
)
|
|
845
|
+
facts[unit.module] = current
|
|
846
|
+
return MappingProxyType(facts)
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
def _unit_import_namespace_writes(
|
|
850
|
+
unit: SourceUnit,
|
|
851
|
+
origins: Mapping[str, frozenset[SymbolRef]],
|
|
852
|
+
resolve: Callable[[SymbolRef], tuple[SymbolRef, ...] | None],
|
|
853
|
+
) -> _ImportNamespaceWrites:
|
|
854
|
+
targets: set[str] = set()
|
|
855
|
+
incomplete = False
|
|
856
|
+
if unit.tree is not None:
|
|
857
|
+
for node in walk_ast(unit.tree):
|
|
858
|
+
reference = _namespace_write_owner(node)
|
|
859
|
+
if reference is None:
|
|
860
|
+
continue
|
|
861
|
+
for origin in _possible_class_symbols(unit, reference, origins):
|
|
862
|
+
resolved = resolve(origin)
|
|
863
|
+
if resolved is None:
|
|
864
|
+
incomplete = True
|
|
865
|
+
else:
|
|
866
|
+
targets.update(symbol.module or symbol.name for symbol in resolved)
|
|
867
|
+
return _ImportNamespaceWrites(frozenset(targets), _runtime_dependency_modules(unit), incomplete)
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
def _namespace_write_owner(node: ast.AST) -> ast.Name | ast.Attribute | None:
|
|
871
|
+
owner: ast.expr | None = None
|
|
872
|
+
match node:
|
|
873
|
+
case ast.Attribute(value=value, ctx=ast.Store() | ast.Del()):
|
|
874
|
+
owner = value
|
|
875
|
+
case ast.Call(func=ast.Name(id="setattr" | "delattr"), args=[value, _, *_]):
|
|
876
|
+
owner = value
|
|
877
|
+
case _:
|
|
878
|
+
pass
|
|
879
|
+
return owner if isinstance(owner, ast.Name | ast.Attribute) else None
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
def _runtime_dependency_modules(unit: SourceUnit) -> frozenset[str]:
|
|
883
|
+
if unit.tree is None:
|
|
884
|
+
return frozenset()
|
|
885
|
+
package = (unit.module or "") if unit.path.name == "__init__.py" else (unit.module or "").rpartition(".")[0]
|
|
886
|
+
dependencies: set[str] = set()
|
|
887
|
+
for statement in _runtime_import_statements(unit.tree):
|
|
888
|
+
match statement:
|
|
889
|
+
case ast.Import(names=aliases):
|
|
890
|
+
for alias in aliases:
|
|
891
|
+
dependencies.update(_module_prefixes(alias.name))
|
|
892
|
+
case ast.ImportFrom(module=module, level=level, names=aliases):
|
|
893
|
+
target = _relative_module(package, level, module)
|
|
894
|
+
if target is None:
|
|
895
|
+
continue
|
|
896
|
+
dependencies.update(_module_prefixes(target))
|
|
897
|
+
dependencies.update(f"{target}.{alias.name}" for alias in aliases if alias.name != "*")
|
|
898
|
+
return frozenset(dependencies)
|
|
899
|
+
|
|
900
|
+
|
|
770
901
|
def _runtime_import_origins(
|
|
771
902
|
unit: SourceUnit, *, node_index: NodeIndex | None = None
|
|
772
903
|
) -> dict[str, frozenset[SymbolRef]]:
|
|
@@ -272,6 +272,7 @@ from sarj_python_lint.rules.pytest_fixture_returns_closed_resource import Pytest
|
|
|
272
272
|
from sarj_python_lint.rules.redundant_test_constructor_forwarder import RedundantTestConstructorForwarder
|
|
273
273
|
from sarj_python_lint.rules.repeated_unimplemented_test_contract import RepeatedUnimplementedTestContract
|
|
274
274
|
from sarj_python_lint.rules.require_exp_on_jwt_decode import RequireExpOnJwtDecode
|
|
275
|
+
from sarj_python_lint.rules.service_singleton_delegates_to_bulk import ServiceSingletonDelegatesToBulk
|
|
275
276
|
from sarj_python_lint.rules.store_field_update_reuses_upsert import StoreFieldUpdateReusesUpsert
|
|
276
277
|
from sarj_python_lint.rules.store_insert_requires_explicit_conflict_policy import (
|
|
277
278
|
StoreInsertRequiresExplicitConflictPolicy,
|
|
@@ -282,6 +283,7 @@ from sarj_python_lint.rules.test_without_explicit_oracle import NoExplicitTestOr
|
|
|
282
283
|
|
|
283
284
|
_AUTHORING_REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
|
|
284
285
|
{
|
|
286
|
+
ServiceSingletonDelegatesToBulk.id: ServiceSingletonDelegatesToBulk,
|
|
285
287
|
NoAsyncClientInConstructor.id: NoAsyncClientInConstructor,
|
|
286
288
|
NoDiscardedTaggedOutcome.id: NoDiscardedTaggedOutcome,
|
|
287
289
|
StoreInsertRequiresExplicitConflictPolicy.id: StoreInsertRequiresExplicitConflictPolicy,
|
|
@@ -196,7 +196,8 @@ def _visible_constructors(
|
|
|
196
196
|
scope_chain: list[int] = []
|
|
197
197
|
current = owner
|
|
198
198
|
while True:
|
|
199
|
-
|
|
199
|
+
if current is owner or not isinstance(current, ast.ClassDef):
|
|
200
|
+
scope_chain.append(id(current))
|
|
200
201
|
parent = parents.get(current)
|
|
201
202
|
if parent is None:
|
|
202
203
|
break
|
|
@@ -498,7 +499,7 @@ def _parse_signal(sql: str) -> str | None:
|
|
|
498
499
|
for statement in parse_postgres(normalized):
|
|
499
500
|
if statement is not None and (signal := _architecture_signal(statement)) is not None:
|
|
500
501
|
return signal
|
|
501
|
-
except SqlglotError:
|
|
502
|
+
except SqlglotError, RecursionError:
|
|
502
503
|
return None
|
|
503
504
|
return None
|
|
504
505
|
|
|
@@ -554,8 +555,17 @@ def _self_join(select: exp.Select, joins: tuple[exp.Join, ...]) -> bool:
|
|
|
554
555
|
from sqlglot import exp # ruff: ignore[import-outside-top-level] -- compare only literal relation identities.
|
|
555
556
|
|
|
556
557
|
from_clause = _expression_arg(select, "from_")
|
|
557
|
-
|
|
558
|
-
|
|
558
|
+
relations = [_expression_arg(from_clause, "this")] if isinstance(from_clause, exp.From) else []
|
|
559
|
+
relations.extend(_expression_arg(join, "this") for join in joins)
|
|
560
|
+
seen: set[tuple[str, ...]] = set()
|
|
561
|
+
for relation in relations:
|
|
562
|
+
key = relation_key(relation)
|
|
563
|
+
if key is None or any(_SQL_HOLE in name for name in key):
|
|
564
|
+
continue
|
|
565
|
+
if key in seen:
|
|
566
|
+
return True
|
|
567
|
+
seen.add(key)
|
|
568
|
+
return False
|
|
559
569
|
|
|
560
570
|
|
|
561
571
|
def _derived_architecture_signal(statement: exp.Expr) -> str | None:
|
|
@@ -647,7 +657,9 @@ def _record_sql_module_imports(node: ast.Import, owner_id: int, constructors: se
|
|
|
647
657
|
|
|
648
658
|
def _query_review_guidance(text_value: str) -> str:
|
|
649
659
|
coordination = re.search(
|
|
650
|
-
r"\b(?:FOR\s+(?:NO\s+KEY\s+)?UPDATE|
|
|
660
|
+
r"\b(?:FOR\s+(?:(?:NO\s+KEY\s+)?UPDATE|(?:KEY\s+)?SHARE)|SKIP\s+LOCKED)\b",
|
|
661
|
+
strip_sql_noise(text_value),
|
|
662
|
+
re.IGNORECASE,
|
|
651
663
|
)
|
|
652
664
|
return (
|
|
653
665
|
" preserve the atomic statement; review bounds, ordering, and locks, then document its cardinality, "
|
|
@@ -61,7 +61,7 @@ class NoCustomControlFlowException(Rule):
|
|
|
61
61
|
autofix=AutofixPolicy.NONE,
|
|
62
62
|
limitations=(
|
|
63
63
|
"This review warning checks stable module-level local Exception/ValueError subclasses whose bodies contain only pass, ellipsis or docstring expressions, including empty local subclasses of those markers. A source-visible direct raise of the class or its constructor is required; imported errors, payload-bearing classes, methods, decorators, generic/ambiguous bases and unused definitions abstain.",
|
|
64
|
-
"Raising a ValueError-derived marker from an import-proven Pydantic field/model validator or legacy validator preserves that rejection protocol. Unknown external bases and visible escapes of the class to registrations, callbacks or other value consumers abstain; HTTPException and other framework-required exception types remain valid.",
|
|
64
|
+
"Raising a ValueError-derived marker from an import-proven Pydantic field/model validator or legacy validator preserves that rejection protocol. Unknown external bases and visible mutations or escapes of the class or a local base to registrations, callbacks or other value consumers abstain; HTTPException and other framework-required exception types remain valid.",
|
|
65
65
|
"Expectedness is not inferred from a name, message or prefix. Genuine exceptional markers may also match and require review rather than conversion. Cross-module callers and registrations, indirect raises, dynamically mutated namespaces and class aliases are not inferred.",
|
|
66
66
|
"Tests, generated and vendor code are excluded. No automatic rewrite is safe: a result return must preserve failure handling and transaction/resource unwinding. SARJ435 separately reviews joined-string reason identities.",
|
|
67
67
|
),
|
|
@@ -114,11 +114,10 @@ class NoCustomControlFlowException(Rule):
|
|
|
114
114
|
if provenance.wildcard or provenance.dynamic_namespace or not provenance.builtin_namespace_untouched:
|
|
115
115
|
return []
|
|
116
116
|
markers = _markers(context, provenance)
|
|
117
|
+
raised_markers = _direct_raises(markers, context, provenance)
|
|
117
118
|
findings: list[Diagnostic] = []
|
|
118
119
|
for marker in markers.values():
|
|
119
|
-
if marker
|
|
120
|
-
continue
|
|
121
|
-
if not _direct_raises(marker, context, provenance):
|
|
120
|
+
if marker not in raised_markers:
|
|
122
121
|
continue
|
|
123
122
|
if is_suppressed(context.source_lines, marker.lineno, self.code):
|
|
124
123
|
continue
|
|
@@ -152,8 +151,13 @@ def _markers(context: PythonFileContext, provenance: ResourceProvenance) -> dict
|
|
|
152
151
|
and provenance.binding(base.id, base) is markers.get(base.id)
|
|
153
152
|
and base.id in markers
|
|
154
153
|
)
|
|
155
|
-
if local or _builtin_base(base, context, provenance):
|
|
156
|
-
|
|
154
|
+
if not (local or _builtin_base(base, context, provenance)):
|
|
155
|
+
continue
|
|
156
|
+
if node.name in provenance.mutated or node.name in provenance.external_bindings:
|
|
157
|
+
continue
|
|
158
|
+
if _escapes(node, context, provenance):
|
|
159
|
+
continue
|
|
160
|
+
markers[node.name] = node
|
|
157
161
|
return markers
|
|
158
162
|
|
|
159
163
|
|
|
@@ -181,14 +185,24 @@ def _builtin_base(base: ast.expr, context: PythonFileContext, provenance: Resour
|
|
|
181
185
|
return any(provenance.imported(base, f"builtins.{name}") for name in _BUILTIN_BASES)
|
|
182
186
|
|
|
183
187
|
|
|
184
|
-
def _direct_raises(
|
|
188
|
+
def _direct_raises(
|
|
189
|
+
markers: dict[str, ast.ClassDef], context: PythonFileContext, provenance: ResourceProvenance
|
|
190
|
+
) -> set[ast.ClassDef]:
|
|
191
|
+
candidates = set(markers.values())
|
|
192
|
+
raised_markers: set[ast.ClassDef] = set()
|
|
193
|
+
value_errors: dict[ast.ClassDef, bool] = {}
|
|
185
194
|
for raised in context.nodes(ast.Raise):
|
|
186
195
|
target = raised.exc.func if isinstance(raised.exc, ast.Call) else raised.exc
|
|
187
|
-
if not isinstance(target, ast.Name)
|
|
196
|
+
if not isinstance(target, ast.Name):
|
|
188
197
|
continue
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
198
|
+
marker = provenance.binding(target.id, target)
|
|
199
|
+
if not isinstance(marker, ast.ClassDef) or marker not in candidates or marker in raised_markers:
|
|
200
|
+
continue
|
|
201
|
+
if marker not in value_errors:
|
|
202
|
+
value_errors[marker] = _value_error_marker(marker, context, provenance)
|
|
203
|
+
if not (value_errors[marker] and _validation_callback(raised, context, provenance)):
|
|
204
|
+
raised_markers.add(marker)
|
|
205
|
+
return raised_markers
|
|
192
206
|
|
|
193
207
|
|
|
194
208
|
def _value_error_marker(marker: ast.ClassDef, context: PythonFileContext, provenance: ResourceProvenance) -> bool:
|
|
@@ -80,7 +80,7 @@ class NoDiscardedTaggedOutcome(ProjectRule):
|
|
|
80
80
|
autofix=AutofixPolicy.NONE,
|
|
81
81
|
limitations=(
|
|
82
82
|
"Review warning for standalone synchronous calls and directly awaited async calls to stable source-visible module functions. The return must resolve to at least two direct Pydantic, dataclass or required TypedDict variants sharing a string Literal discriminator with disjoint values. No name such as Result or Error establishes proof.",
|
|
83
|
-
"Module-local and prepared-project absolute imports, quoted bare aliases and PEP 604 returns, unquoted typing.Union and Annotated, and bounded non-generic type aliases are supported. Quoted typing-wrapper combinations, unknown receivers, decorated or generic functions, re-exports, optional/scalar branches, generic records, inheritance, dynamic namespaces, rebound identities and unsupported annotations abstain.",
|
|
83
|
+
"Module-local and prepared-project absolute imports, quoted bare aliases and PEP 604 returns, unquoted typing.Union and Annotated, and bounded non-generic type aliases are supported. Quoted typing-wrapper combinations, unknown receivers, decorated or generic functions, re-exports, optional/scalar branches, generic records, inheritance, dynamic namespaces, rebound or escaped callable identities, generator functions and unsupported annotations abstain.",
|
|
84
84
|
"Named assignments, returned, passed or conditionally consumed results are preserved. A direct assignment to the single underscore target is an explicit discard and is reviewed. An unawaited async call belongs to coroutine checks. Direct class-body execution, generated sources and opaque providers are excluded. This rule identifies discarding; it does not prove that an assignment handles every variant or infer expected failure from a record name.",
|
|
85
85
|
"String Literal tags must be direct instance fields on every variant and retain their original class-scope binding. Quoted field tags remain outside the proof; quoted module return annotations retain their source declaration anchor. Dynamic fields, optional TypedDict fields, non-string tags, computed Literal values and multi-field discriminator hierarchies are not inferred. No automatic rewrite changes call behavior or error handling.",
|
|
86
86
|
),
|
|
@@ -145,6 +145,7 @@ class _OutcomeCalls:
|
|
|
145
145
|
self.contracts = DeclaredContracts(context)
|
|
146
146
|
self.providers: dict[Path, PythonFileContext] = {context.path: context}
|
|
147
147
|
self.outcomes: dict[_Function, bool] = {}
|
|
148
|
+
self.generator_owners: dict[Path, set[ast.AST | None]] = {}
|
|
148
149
|
|
|
149
150
|
def discarded(self, statement: _Discard) -> bool:
|
|
150
151
|
value = statement.value
|
|
@@ -162,11 +163,21 @@ class _OutcomeCalls:
|
|
|
162
163
|
members = (
|
|
163
164
|
self._members(_Reference(source.context, function.returns, function), frozenset())
|
|
164
165
|
if function.returns is not None
|
|
166
|
+
and _callable_identity(source.context, function)
|
|
167
|
+
and self._non_generator(source)
|
|
165
168
|
else None
|
|
166
169
|
)
|
|
167
170
|
self.outcomes[function] = members is not None and self._tagged(members)
|
|
168
171
|
return self.outcomes[function]
|
|
169
172
|
|
|
173
|
+
def _non_generator(self, source: _FunctionSource) -> bool:
|
|
174
|
+
owner = source.context
|
|
175
|
+
if owner.path not in self.generator_owners:
|
|
176
|
+
self.generator_owners[owner.path] = {
|
|
177
|
+
owner.static_resource_provenance.scope(node) for node in owner.nodes(ast.Yield, ast.YieldFrom)
|
|
178
|
+
}
|
|
179
|
+
return source.function not in self.generator_owners[owner.path]
|
|
180
|
+
|
|
170
181
|
def _function_source(self, expression: ast.expr) -> _FunctionSource | None:
|
|
171
182
|
provenance = self.context.static_resource_provenance
|
|
172
183
|
if provenance.dynamic_namespace or provenance.wildcard:
|
|
@@ -325,6 +336,44 @@ class _OutcomeCalls:
|
|
|
325
336
|
return _record_tags(declaration, provider)
|
|
326
337
|
|
|
327
338
|
|
|
339
|
+
def _callable_identity(context: PythonFileContext, function: _Function) -> bool:
|
|
340
|
+
provenance = context.static_resource_provenance
|
|
341
|
+
pending: list[_Function | ast.Name] = [function]
|
|
342
|
+
seen: set[ast.AST] = set()
|
|
343
|
+
while pending:
|
|
344
|
+
binding = pending.pop()
|
|
345
|
+
if binding in seen:
|
|
346
|
+
continue
|
|
347
|
+
if len(seen) >= _MAX_DEPTH:
|
|
348
|
+
return False
|
|
349
|
+
seen.add(binding)
|
|
350
|
+
name = binding.id if isinstance(binding, ast.Name) else binding.name
|
|
351
|
+
if name in provenance.mutated or name in provenance.external_bindings:
|
|
352
|
+
return False
|
|
353
|
+
for read in provenance.binding_reads(name, binding):
|
|
354
|
+
parent = context.parents.get(read)
|
|
355
|
+
if isinstance(parent, ast.Call) and parent.func is read:
|
|
356
|
+
continue
|
|
357
|
+
target = _simple_callable_alias(context, read)
|
|
358
|
+
if target is None:
|
|
359
|
+
return False
|
|
360
|
+
pending.append(target)
|
|
361
|
+
return True
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def _simple_callable_alias(context: PythonFileContext, read: ast.Name) -> ast.Name | None:
|
|
365
|
+
assignment = context.parents.get(read)
|
|
366
|
+
if not isinstance(assignment, ast.Assign | ast.AnnAssign) or assignment.value is not read:
|
|
367
|
+
return None
|
|
368
|
+
if not isinstance(context.parents.get(assignment), ast.Module | ast.FunctionDef | ast.AsyncFunctionDef):
|
|
369
|
+
return None
|
|
370
|
+
targets = assignment.targets if isinstance(assignment, ast.Assign) else [assignment.target]
|
|
371
|
+
if len(targets) != 1 or not isinstance(targets[0], ast.Name):
|
|
372
|
+
return None
|
|
373
|
+
target = targets[0]
|
|
374
|
+
return target if context.static_resource_provenance.binding(target.id, target) is target else None
|
|
375
|
+
|
|
376
|
+
|
|
328
377
|
def _union_children(node: ast.expr, context: PythonFileContext) -> list[ast.expr] | None:
|
|
329
378
|
if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
|
|
330
379
|
return [node.left, node.right]
|