sarj-python-lint 1.44.0__tar.gz → 1.45.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.44.0 → sarj_python_lint-1.45.0}/PKG-INFO +1 -1
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/pyproject.toml +1 -1
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/_file_context.py +1 -1
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rule_base.py +1 -1
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_annotation_semantics.py +1 -1
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_comments.py +4 -1
- sarj_python_lint-1.45.0/src/sarj_python_lint/rules/_inherited_contract.py +7 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_paths.py +1 -1
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_registry.py +2 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_invalid_pydantic_bypass.py +174 -22
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_psycopg_execution_outside_injected_owner.py +1 -1
- sarj_python_lint-1.45.0/src/sarj_python_lint/rules/no_shallow_record_type_guard.py +388 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_namedtuple_over_tuple_return.py +4 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_nominal_id_types.py +5 -1
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_non_nullable_collection.py +1 -1
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_struct_over_namedtuple.py +96 -1
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/pytest_fixture_returns_bare_tuple.py +119 -28
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/.gitignore +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/LICENSE +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/README.md +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/__init__.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/__main__.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/_analysis_session.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/_filesystem.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/_python_target.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/_ratchet_cli.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/_secret_names.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/_source.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/_version.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/benchmark.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/interpreter_argv.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/json_boundary.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/py.typed +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/ratchet.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/__init__.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_ast_index.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_ast_position.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_closed_helpers.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_contract_annotation.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_copy_on_write.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_dict_key_views.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_docstrings.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_exports.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_fastapi.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_first_party.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_fixed_record.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_imports.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_inert_model_annotation.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_local_source.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_logging.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_namespace_access.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_nominal_project.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_project_index.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_prose_budget.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_pydantic_field_contract.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_pytest.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_resolved_operation_signature.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_resource_provenance.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_set_fields.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_sql.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_static_json_alias.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_suppression_comments.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_test_assertions.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_test_composition.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_test_provenance.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/_type_guard_symbols.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/async_cleanup_registered_synchronously.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/async_mock_call_without_await_assertion.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/complex_postgres_query_requires_architecture_review.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/defect_xfail_requires_explicit_strict.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/docstring_args_restate_signature.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/docstring_returns_restate_signature.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/duplicate_adjacent_exception_handler.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/duplicate_closed_pure_helper.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/excessive_commentary.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/fakes_in_shared_location.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/fastapi_class_router_contract.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/fastapi_explicit_openapi_contract.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/iac_source_coupled_test.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/invalid_pydantic_field_default.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/mock_without_spec.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/named_record_at_boundaries.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/negative_only_http_status_assertion.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_analytical_aggregation_in_postgres_store.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_any_mapping_types.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_async_function_cache.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_before_validator_input_mutation.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_broad_keyword_capture.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_comment_cruft.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_conflicting_pydantic_alias_validation.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_conftest_test_module_import.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_contradictory_type_guard.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_copied_inherited_docstring.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_cors_wildcard_with_credentials.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_database_programmability.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_delete_statement.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_dunder_all.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_duplicate_dunder_all_entry.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_excessive_cognitive_complexity.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_fastapi_on_event.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_file_level_escape_hatch_suppression.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_first_party_private_import.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_fixed_sleep_before_assert.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_fixed_sleep_in_loop.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_frozen_after_validator_field_write.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_generic_single_export_module.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_hidden_constructor_fallback.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_impossible_pydantic_field_bounds.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_injected_module_loader.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_input_model_mutation.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_interpreter_source_arguments.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_invalid_argument_name_suppression.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_loguru_exc_info.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_loguru_printf_format.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_mocked_pydantic_value_object.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_nested_pydantic_field_validator.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_nullable_dependency_fallback.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_offset_pagination.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_partial_type_is.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_positional_psycopg_row_escape.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_random_uuid_in_sql.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_raw_connection_in_tests.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_raw_source_text_test_oracle.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_redundant_literal_description.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_redundant_module_alias_exports.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_repeated_string_literal.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_repeated_test_body.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_restated_comment.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_secret_in_log.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_select_star.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_service_behavior_in_settings.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_shallow_container_type_guard.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_string_concat_in_loop.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_swallowed_asyncio_cancellation.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_tautological_expect.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_test_method_grafting.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_typed_doc_sections.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_unique_violation_message_match.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_unknown_pydantic_validator_field.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_unnecessary_docstring.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_unused_copy_result.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_unused_underscored_keyword_parameter.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_unused_value_marker.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_vague_suppression_description.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/no_whole_request_response_payload_in_log.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/opaque_parametrize_case_needs_id.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/over_mocked_test.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/phase_label_comment.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_autospec_for_callable_mock.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_class_row.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_collection_comprehension.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_constant_time_secret_compare.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_declarative_batch_validation.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_discriminated_model_union.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_fstring_over_concat.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_immutable_module_constant.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_injected_dependency_over_monkeypatch.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_library_fake.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_match_assert_never.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_match_exception_dispatch.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_match_type_dispatch.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_match_value_dispatch.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_module_level_constant.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_monkeypatch_for_process_state_in_test.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_monotonic_for_elapsed_time.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_native_string_check.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_or_pattern.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_pydantic_json_value.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_pytest_fixture_injection.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_regex_fullmatch.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_required_constructor_parameters.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_self_documenting_constant.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_self_type_annotation.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_set_isdisjoint.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_str_enum.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_walrus_awaited_none_guard.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_walrus_comprehension_filter.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_walrus_regex_match.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/prefer_walrus_stream_loop.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/production_derived_test_cases.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/redundant_class_docstring.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/redundant_docstring.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/redundant_module_docstring.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/redundant_test_constructor_forwarder.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/repeated_kwarg_heavy_call_in_test.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/repeated_static_call_cases.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/repeated_test_composition.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/repeated_unimplemented_test_contract.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/require_explicit_contract_implementation.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/require_explicit_psycopg_transaction.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/require_explicit_service_contract.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/require_injectable_retry_sleep.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/require_keyword_only_swap_prone_params.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/require_nodecode_for_splitting_settings_field.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/require_port_for_service.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/require_precise_factory_signature.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/require_public_dependency_contract.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/require_pydantic_for_external_json.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/require_pydantic_for_structured_payload.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/require_pydantic_ordinal_lower_bound.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/restated_test_docstring.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/shared_mutable_pydantic_factory.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/stepdown.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/store_get_delegates_to_bulk_read.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/store_insert_requires_on_conflict.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/subprocess_kill_without_reap.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/test_without_explicit_oracle.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/timestamp_order_requires_tiebreaker.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/trailing_value_narration.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/typed_error_reasons.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/uncontrolled_randomness_in_test.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/unused_mock_setup.py +0 -0
- {sarj_python_lint-1.44.0 → sarj_python_lint-1.45.0}/src/sarj_python_lint/rules/unused_test_factory_option.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sarj-python-lint
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.45.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/
|
|
@@ -338,7 +338,10 @@ def _scan_memo(source: str) -> _Scan:
|
|
|
338
338
|
global _last_scan # ruff: ignore[global-statement] — single-slot memo; the CLI runs rules per file sequentially
|
|
339
339
|
if _last_scan is not None and _last_scan[0] is source:
|
|
340
340
|
return _last_scan[1]
|
|
341
|
-
|
|
341
|
+
try:
|
|
342
|
+
result = _scan(source)
|
|
343
|
+
except UnicodeError:
|
|
344
|
+
result = _Scan([], [], set(), 1 << 30, [])
|
|
342
345
|
_last_scan = (source, result)
|
|
343
346
|
return result
|
|
344
347
|
|
|
@@ -81,7 +81,7 @@ def _leading_header_bodies(source: str) -> list[str]:
|
|
|
81
81
|
token = next(tokens)
|
|
82
82
|
except StopIteration:
|
|
83
83
|
break
|
|
84
|
-
except IndentationError, tokenize.TokenError:
|
|
84
|
+
except IndentationError, tokenize.TokenError, UnicodeError:
|
|
85
85
|
break
|
|
86
86
|
if token.start[0] > _GENERATED_HEADER_LINES:
|
|
87
87
|
break
|
|
@@ -218,6 +218,7 @@ from sarj_python_lint.rules.no_contradictory_type_guard import NoContradictoryTy
|
|
|
218
218
|
from sarj_python_lint.rules.no_impossible_pydantic_field_bounds import NoImpossiblePydanticFieldBounds
|
|
219
219
|
from sarj_python_lint.rules.no_invalid_pydantic_bypass import NoInvalidPydanticBypass
|
|
220
220
|
from sarj_python_lint.rules.no_partial_type_is import NoPartialTypeIs
|
|
221
|
+
from sarj_python_lint.rules.no_shallow_record_type_guard import NoShallowRecordTypeGuard
|
|
221
222
|
from sarj_python_lint.rules.no_unknown_pydantic_validator_field import NoUnknownPydanticValidatorField
|
|
222
223
|
from sarj_python_lint.rules.prefer_discriminated_model_union import PreferDiscriminatedModelUnion
|
|
223
224
|
from sarj_python_lint.rules.redundant_test_constructor_forwarder import RedundantTestConstructorForwarder
|
|
@@ -232,6 +233,7 @@ REGISTRY: Mapping[str, type[Rule]] = MappingProxyType(
|
|
|
232
233
|
NoInvalidPydanticBypass.id: NoInvalidPydanticBypass,
|
|
233
234
|
NoPartialTypeIs.id: NoPartialTypeIs,
|
|
234
235
|
NoContradictoryTypeGuard.id: NoContradictoryTypeGuard,
|
|
236
|
+
NoShallowRecordTypeGuard.id: NoShallowRecordTypeGuard,
|
|
235
237
|
NoConflictingPydanticAliasValidation.id: NoConflictingPydanticAliasValidation,
|
|
236
238
|
NoUnknownPydanticValidatorField.id: NoUnknownPydanticValidatorField,
|
|
237
239
|
NoImpossiblePydanticFieldBounds.id: NoImpossiblePydanticFieldBounds,
|
|
@@ -4,8 +4,9 @@ import ast
|
|
|
4
4
|
from dataclasses import dataclass
|
|
5
5
|
from itertools import starmap
|
|
6
6
|
from pathlib import PurePosixPath
|
|
7
|
-
from typing import TYPE_CHECKING, final, override
|
|
7
|
+
from typing import TYPE_CHECKING, Final, TypeIs, final, override
|
|
8
8
|
|
|
9
|
+
from sarj_python_lint.json_boundary import is_object_mapping
|
|
9
10
|
from sarj_python_lint.rule_base import (
|
|
10
11
|
AutofixPolicy,
|
|
11
12
|
Diagnostic,
|
|
@@ -40,6 +41,9 @@ _PASSIVE_CONFIG = frozenset({"frozen", "strict", "validate_default"})
|
|
|
40
41
|
_MODEL_APIS = frozenset({"model_construct", "model_validate", "model_validate_json", "model_validate_strings"})
|
|
41
42
|
_MAX_ANNOTATION_DEPTH = 64
|
|
42
43
|
_MAX_ANNOTATION_NODES = 1024
|
|
44
|
+
_MAX_LITERAL_TEXT = 65536
|
|
45
|
+
_UNKNOWN_LITERAL: Final = object()
|
|
46
|
+
_PAIR_ARITY = 2
|
|
43
47
|
|
|
44
48
|
|
|
45
49
|
@dataclass(frozen=True, slots=True)
|
|
@@ -75,11 +79,12 @@ class NoInvalidPydanticBypass(Rule):
|
|
|
75
79
|
autofix=AutofixPolicy.NONE,
|
|
76
80
|
limitations=(
|
|
77
81
|
"Only plain, direct, module-level Pydantic BaseModel subclasses with supported built-in and typing field annotations are inspected; hooks, inheritance, class decorators and custom methods are excluded.",
|
|
78
|
-
"
|
|
82
|
+
"Checks explicit None conflicts, closed string Literal domains, and bounded side-effect-free literal values contradicting builtin primitive or container field contracts. Coercible input is not assumed to be validated stored output.",
|
|
83
|
+
"Container literals preserve their actual construction semantics, including last-wins dictionary keys. Unpacking, calls, unhashable literal containers, opaque values, numeric Literal domains, bounds, aliases and custom field types are not inferred. Bool remains an int subtype, and numeric int-to-float/complex assignability is preserved.",
|
|
79
84
|
"model_construct must use explicit keywords; omitted required fields are checked only when no unpacking or positional argument can supply them.",
|
|
80
85
|
"model_copy requires a fresh exact constructor or a stable unescaped local constructor binding, plus a plain literal update dictionary. Arbitrary annotated receivers and overridden methods are excluded.",
|
|
81
86
|
"Field aliases, non-passive model configuration, class or instance mutation and escape, ambiguous namespaces, tests and generated files are excluded.",
|
|
82
|
-
"Direct model field annotations exceeding a bounded depth or node budget exclude the file rather than risking recursive analysis failure.",
|
|
87
|
+
"Direct model field annotations exceeding a bounded depth or node budget exclude the file rather than risking recursive analysis failure. Literal analysis also bounds the value tree and combined string/bytes content to avoid excessive witness work.",
|
|
83
88
|
"Trusted valid construction and copy operations remain legal. No autofix chooses domain values or changes validation behavior.",
|
|
84
89
|
),
|
|
85
90
|
examples=(
|
|
@@ -111,6 +116,36 @@ class NoInvalidPydanticBypass(Rule):
|
|
|
111
116
|
expected_count=0,
|
|
112
117
|
public=True,
|
|
113
118
|
),
|
|
119
|
+
RuleExample(
|
|
120
|
+
example_id="construct-invalid-list-member",
|
|
121
|
+
scenario="stored-literal-contract",
|
|
122
|
+
title="Unchecked list elements must satisfy the stored field type",
|
|
123
|
+
outcome=ExampleOutcome.MATCH,
|
|
124
|
+
files=(
|
|
125
|
+
ExampleFile.python(
|
|
126
|
+
"app/models.py",
|
|
127
|
+
"from pydantic import BaseModel\nclass Payload(BaseModel):\n labels: list[str]\npayload = Payload.model_construct(labels=[3])\n",
|
|
128
|
+
),
|
|
129
|
+
),
|
|
130
|
+
focus_path=PurePosixPath("app/models.py"),
|
|
131
|
+
expected_count=1,
|
|
132
|
+
public=True,
|
|
133
|
+
),
|
|
134
|
+
RuleExample(
|
|
135
|
+
example_id="construct-trusted-list-members",
|
|
136
|
+
scenario="stored-literal-contract",
|
|
137
|
+
title="Trusted list elements preserve the field contract",
|
|
138
|
+
outcome=ExampleOutcome.NO_MATCH,
|
|
139
|
+
files=(
|
|
140
|
+
ExampleFile.python(
|
|
141
|
+
"app/models.py",
|
|
142
|
+
"from pydantic import BaseModel\nclass Payload(BaseModel):\n labels: list[str]\npayload = Payload.model_construct(labels=['ready'])\n",
|
|
143
|
+
),
|
|
144
|
+
),
|
|
145
|
+
focus_path=PurePosixPath("app/models.py"),
|
|
146
|
+
expected_count=0,
|
|
147
|
+
public=True,
|
|
148
|
+
),
|
|
114
149
|
),
|
|
115
150
|
)
|
|
116
151
|
description = documentation.summary
|
|
@@ -161,7 +196,10 @@ def _bounded_annotation(annotation: ast.expr) -> bool:
|
|
|
161
196
|
visited += 1
|
|
162
197
|
if depth > _MAX_ANNOTATION_DEPTH or visited > _MAX_ANNOTATION_NODES:
|
|
163
198
|
return False
|
|
164
|
-
|
|
199
|
+
for child in ast.iter_child_nodes(node):
|
|
200
|
+
if visited + len(pending) >= _MAX_ANNOTATION_NODES:
|
|
201
|
+
return False
|
|
202
|
+
pending.append((child, depth + 1))
|
|
165
203
|
return True
|
|
166
204
|
|
|
167
205
|
|
|
@@ -423,28 +461,142 @@ def _invalid_values(
|
|
|
423
461
|
)
|
|
424
462
|
for name, value in values.items():
|
|
425
463
|
field = model.fields.get(name)
|
|
426
|
-
if field is None
|
|
464
|
+
if field is None:
|
|
427
465
|
continue
|
|
428
|
-
|
|
429
|
-
if
|
|
430
|
-
findings.append(
|
|
431
|
-
_diagnostic(
|
|
432
|
-
value,
|
|
433
|
-
context,
|
|
434
|
-
f"Unchecked `{name}={value.value!r}` is outside `{model.node.name}`'s declared string Literal domain; validate a complete input mapping or supply an admitted value.",
|
|
435
|
-
)
|
|
436
|
-
)
|
|
437
|
-
elif value.value is None and _excludes_none(field.annotation, context):
|
|
438
|
-
findings.append(
|
|
439
|
-
_diagnostic(
|
|
440
|
-
value,
|
|
441
|
-
context,
|
|
442
|
-
f"Unchecked `{name}=None` contradicts `{model.node.name}`'s non-null field annotation; validate a complete input mapping or supply a non-null value.",
|
|
443
|
-
)
|
|
444
|
-
)
|
|
466
|
+
finding = _invalid_field_value(model, name, field.annotation, value, context)
|
|
467
|
+
if finding is not None:
|
|
468
|
+
findings.append(finding)
|
|
445
469
|
return findings
|
|
446
470
|
|
|
447
471
|
|
|
472
|
+
def _invalid_field_value(
|
|
473
|
+
model: _Model, name: str, annotation: ast.expr, value: ast.expr, context: PythonFileContext
|
|
474
|
+
) -> Diagnostic | None:
|
|
475
|
+
literal = _literal_value(value)
|
|
476
|
+
if literal is _UNKNOWN_LITERAL:
|
|
477
|
+
return None
|
|
478
|
+
if not _bounded_literal_text(annotation, value):
|
|
479
|
+
return None
|
|
480
|
+
domain = _string_domain(annotation, context)
|
|
481
|
+
if domain is not None and not any(literal == member for member in domain):
|
|
482
|
+
message = f"Unchecked `{name}` is outside `{model.node.name}`'s declared string Literal domain; validate a complete input mapping or supply an admitted value."
|
|
483
|
+
elif literal is None and _excludes_none(annotation, context):
|
|
484
|
+
message = f"Unchecked `{name}=None` contradicts `{model.node.name}`'s non-null field annotation; validate a complete input mapping or supply a non-null value."
|
|
485
|
+
elif _literal_incompatible(annotation, literal, context):
|
|
486
|
+
message = f"Unchecked `{name}` stores a literal incompatible with `{model.node.name}`'s field type; validate a complete input mapping or supply a value satisfying the stored contract."
|
|
487
|
+
else:
|
|
488
|
+
return None
|
|
489
|
+
return _diagnostic(value, context, message)
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
def _bounded_literal_text(annotation: ast.expr, value: ast.expr) -> bool:
|
|
493
|
+
length = 0
|
|
494
|
+
for expression in (annotation, value):
|
|
495
|
+
for node in ast.walk(expression):
|
|
496
|
+
if isinstance(node, ast.Constant) and isinstance(node.value, str | bytes):
|
|
497
|
+
length += len(node.value)
|
|
498
|
+
if length > _MAX_LITERAL_TEXT:
|
|
499
|
+
return False
|
|
500
|
+
return True
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
def _literal_value(node: ast.expr) -> object:
|
|
504
|
+
if not _bounded_annotation(node) or any(isinstance(member, ast.Call) for member in ast.walk(node)):
|
|
505
|
+
return _UNKNOWN_LITERAL
|
|
506
|
+
try:
|
|
507
|
+
return ast.literal_eval(node) # pyright: ignore[reportAny] -- bounded stdlib literal parser boundary exports object.
|
|
508
|
+
except ValueError, TypeError, SyntaxError, RecursionError:
|
|
509
|
+
return _UNKNOWN_LITERAL
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
def _literal_incompatible(annotation: ast.expr, value: object, context: PythonFileContext) -> bool:
|
|
513
|
+
if isinstance(annotation, ast.Constant):
|
|
514
|
+
return annotation.value is None and value is not None
|
|
515
|
+
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
|
|
516
|
+
return _literal_incompatible(annotation.left, value, context) and _literal_incompatible(
|
|
517
|
+
annotation.right, value, context
|
|
518
|
+
)
|
|
519
|
+
if isinstance(annotation, ast.Name) and annotation.id in _BUILTINS:
|
|
520
|
+
return _builtin_incompatible(annotation.id, value)
|
|
521
|
+
if not isinstance(annotation, ast.Subscript):
|
|
522
|
+
return False
|
|
523
|
+
return _subscript_incompatible(annotation, value, context)
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def _subscript_incompatible(annotation: ast.Subscript, value: object, context: PythonFileContext) -> bool:
|
|
527
|
+
members = _members(annotation.slice)
|
|
528
|
+
if _typing_wrapper(annotation, "Annotated", context):
|
|
529
|
+
return bool(members) and _literal_incompatible(members[0], value, context)
|
|
530
|
+
if _typing_wrapper(annotation, "Optional", context):
|
|
531
|
+
return value is not None and _literal_incompatible(annotation.slice, value, context)
|
|
532
|
+
if _typing_wrapper(annotation, "Union", context):
|
|
533
|
+
return bool(members) and all(_literal_incompatible(member, value, context) for member in members)
|
|
534
|
+
domain = _string_domain(annotation, context)
|
|
535
|
+
if domain is not None:
|
|
536
|
+
return not any(value == member for member in domain)
|
|
537
|
+
if isinstance(annotation.value, ast.Name) and annotation.value.id in _GENERICS:
|
|
538
|
+
return _container_incompatible(annotation.value.id, members, value, context)
|
|
539
|
+
return False
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def _builtin_incompatible(kind: str, value: object) -> bool:
|
|
543
|
+
match kind:
|
|
544
|
+
case "str":
|
|
545
|
+
return not isinstance(value, str)
|
|
546
|
+
case "bytes":
|
|
547
|
+
return not isinstance(value, bytes)
|
|
548
|
+
case "bool":
|
|
549
|
+
return not isinstance(value, bool)
|
|
550
|
+
case "int":
|
|
551
|
+
return not isinstance(value, int)
|
|
552
|
+
case "float":
|
|
553
|
+
return not isinstance(value, int | float)
|
|
554
|
+
case "complex":
|
|
555
|
+
return not isinstance(value, int | float | complex)
|
|
556
|
+
case "list":
|
|
557
|
+
return not isinstance(value, list)
|
|
558
|
+
case "dict":
|
|
559
|
+
return not isinstance(value, dict)
|
|
560
|
+
case "set":
|
|
561
|
+
return not isinstance(value, set)
|
|
562
|
+
case "frozenset":
|
|
563
|
+
return not isinstance(value, frozenset)
|
|
564
|
+
case "tuple":
|
|
565
|
+
return not isinstance(value, tuple)
|
|
566
|
+
case _:
|
|
567
|
+
return False
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
def _container_incompatible(kind: str, members: list[ast.expr], value: object, context: PythonFileContext) -> bool:
|
|
571
|
+
if _builtin_incompatible(kind, value):
|
|
572
|
+
return True
|
|
573
|
+
if kind == "dict" and is_object_mapping(value) and len(members) == _PAIR_ARITY:
|
|
574
|
+
return any(
|
|
575
|
+
_literal_incompatible(members[0], key, context) or _literal_incompatible(members[1], item, context)
|
|
576
|
+
for key, item in value.items()
|
|
577
|
+
)
|
|
578
|
+
if _is_literal_items(value):
|
|
579
|
+
if kind == "tuple" and isinstance(value, tuple):
|
|
580
|
+
return _tuple_incompatible(members, value, context)
|
|
581
|
+
if kind in {"list", "set", "frozenset"} and len(members) == 1:
|
|
582
|
+
return any(_literal_incompatible(members[0], item, context) for item in value)
|
|
583
|
+
return False
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
def _is_literal_items(value: object) -> TypeIs[list[object] | set[object] | frozenset[object] | tuple[object, ...]]:
|
|
587
|
+
return isinstance(value, list | set | frozenset | tuple)
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
def _tuple_incompatible(members: list[ast.expr], value: tuple[object, ...], context: PythonFileContext) -> bool:
|
|
591
|
+
if len(members) == _PAIR_ARITY and _ellipsis(members[1]):
|
|
592
|
+
return any(_literal_incompatible(members[0], item, context) for item in value)
|
|
593
|
+
if any(_ellipsis(member) for member in members):
|
|
594
|
+
return False
|
|
595
|
+
return len(members) != len(value) or any(
|
|
596
|
+
_literal_incompatible(member, item, context) for member, item in zip(members, value, strict=False)
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
|
|
448
600
|
def _string_domain(annotation: ast.expr, context: PythonFileContext) -> frozenset[str | None] | None:
|
|
449
601
|
if isinstance(annotation, ast.Constant) and annotation.value is None:
|
|
450
602
|
return frozenset({None})
|
|
@@ -848,6 +848,6 @@ def _module_type_aliases(tree: ast.Module, counts: dict[str, int]) -> dict[str,
|
|
|
848
848
|
def _string_annotation_kind(value: str, types: _TypeIndex, seen: frozenset[str]) -> str | None:
|
|
849
849
|
try:
|
|
850
850
|
parsed = ast.parse(value, mode="eval")
|
|
851
|
-
except SyntaxError:
|
|
851
|
+
except SyntaxError, UnicodeError:
|
|
852
852
|
return None
|
|
853
853
|
return _annotation_kind(parsed.body, types, seen)
|