From 5ac9b96ab36c43429bd7c081cadccd5192628273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Gr=C3=B6nholm?= Date: Wed, 16 Aug 2023 00:43:14 +0300 Subject: [PATCH] Fixed annotated variable assignments broken with suppress_type_checks() Fixes #380. --- docs/versionhistory.rst | 5 ++++ src/typeguard/_functions.py | 50 +++++++++++++++++------------------ tests/test_instrumentation.py | 12 ++++++++- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/docs/versionhistory.rst b/docs/versionhistory.rst index e57f6f6..ad98df8 100644 --- a/docs/versionhistory.rst +++ b/docs/versionhistory.rst @@ -3,6 +3,11 @@ Version history This library adheres to `Semantic Versioning 2.0 `_. +**UNRELEASED** + +- Fixed ``suppress_type_checks()`` causing annotated variable assignments to always + assign ``None`` (`#380 `_) + **4.1.0** (2023-07-30) - Added support for passing a tuple as ``expected_type`` to ``check_type()``, making it diff --git a/src/typeguard/_functions.py b/src/typeguard/_functions.py index f851e00..db55e5a 100644 --- a/src/typeguard/_functions.py +++ b/src/typeguard/_functions.py @@ -247,7 +247,7 @@ def check_variable_assignment( value: object, varname: str, annotation: Any, memo: TypeCheckMemo ) -> Any: if _suppression.type_checks_suppressed: - return + return value try: check_type_internal(value, annotation, memo) @@ -265,36 +265,36 @@ def check_variable_assignment( def check_multi_variable_assignment( value: Any, targets: list[dict[str, Any]], memo: TypeCheckMemo ) -> Any: - if _suppression.type_checks_suppressed: - return - if max(len(target) for target in targets) == 1: iterated_values = [value] else: iterated_values = list(value) - for expected_types in targets: - value_index = 0 - for ann_index, (varname, expected_type) in enumerate(expected_types.items()): - if varname.startswith("*"): - varname = varname[1:] - keys_left = len(expected_types) - 1 - ann_index - next_value_index = len(iterated_values) - keys_left - obj: object = iterated_values[value_index:next_value_index] - value_index = next_value_index - else: - obj = iterated_values[value_index] - value_index += 1 - - try: - check_type_internal(obj, expected_type, memo) - except TypeCheckError as exc: - qualname = qualified_name(obj, add_class_prefix=True) - exc.append_path_element(f"value assigned to {varname} ({qualname})") - if memo.config.typecheck_fail_callback: - memo.config.typecheck_fail_callback(exc, memo) + if not _suppression.type_checks_suppressed: + for expected_types in targets: + value_index = 0 + for ann_index, (varname, expected_type) in enumerate( + expected_types.items() + ): + if varname.startswith("*"): + varname = varname[1:] + keys_left = len(expected_types) - 1 - ann_index + next_value_index = len(iterated_values) - keys_left + obj: object = iterated_values[value_index:next_value_index] + value_index = next_value_index else: - raise + obj = iterated_values[value_index] + value_index += 1 + + try: + check_type_internal(obj, expected_type, memo) + except TypeCheckError as exc: + qualname = qualified_name(obj, add_class_prefix=True) + exc.append_path_element(f"value assigned to {varname} ({qualname})") + if memo.config.typecheck_fail_callback: + memo.config.typecheck_fail_callback(exc, memo) + else: + raise return iterated_values[0] if len(iterated_values) == 1 else iterated_values diff --git a/tests/test_instrumentation.py b/tests/test_instrumentation.py index c75f841..74bab3e 100644 --- a/tests/test_instrumentation.py +++ b/tests/test_instrumentation.py @@ -8,7 +8,7 @@ import pytest from pytest import FixtureRequest -from typeguard import TypeCheckError, config, install_import_hook +from typeguard import TypeCheckError, config, install_import_hook, suppress_type_checks from typeguard._importhook import OPTIMIZATION pytestmark = pytest.mark.filterwarnings("error:no type annotations present") @@ -332,3 +332,13 @@ def test_literal_in_union(dummymodule): def test_typevar_forwardref(dummymodule): instance = dummymodule.typevar_forwardref(dummymodule.DummyClass) assert isinstance(instance, dummymodule.DummyClass) + + +def test_suppress_annotated_assignment(dummymodule): + with suppress_type_checks(): + assert dummymodule.literal_in_union("foo") == "foo" + + +def test_suppress_annotated_multi_assignment(dummymodule): + with suppress_type_checks(): + assert dummymodule.multi_assign_single_value() == (6, 6, 6)