From 21c6162dcf8b41fd2f543bcd1465af9c8553fa01 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Thu, 25 Aug 2022 22:20:19 -0700 Subject: [PATCH 1/3] Make allow_call work with Annotated literals --- docs/changelog.md | 2 ++ pyanalyze/signature.py | 48 +++++++++++++++++++++++-------------- pyanalyze/test_signature.py | 11 +++++++++ 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 867786d3..a687f515 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,6 +2,8 @@ ## Unreleased +- `allow_call` callables are now also called if the arguments + are literals wrapped in `Annotated` (#540) - Support Python 3.11 (#537) - Fix type checking of binary operators involving unions (#531) - Improve `TypeVar` solution heuristic for constrained diff --git a/pyanalyze/signature.py b/pyanalyze/signature.py index 85ffc6b8..a064e588 100644 --- a/pyanalyze/signature.py +++ b/pyanalyze/signature.py @@ -1254,41 +1254,45 @@ def _maybe_perform_call( args = [] kwargs = {} for definitely_present, composite in actual_args.positionals: - if definitely_present and isinstance(composite.value, KnownValue): - args.append(composite.value.val) - else: + if not definitely_present: + return None + arg = _extract_known_value(composite.value) + if arg is None: return None + args.append(arg.val) if actual_args.star_args is not None: values = concrete_values_from_iterable( actual_args.star_args, ctx.can_assign_ctx ) - if isinstance(values, collections.abc.Sequence) and all_of_type( - values, KnownValue - ): - args += [val.val for val in values] - else: + if not isinstance(values, collections.abc.Sequence): return None + for args_val in values: + arg = _extract_known_value(args_val) + if arg is None: + return None + args.append(arg.val) for kwarg, (required, composite) in actual_args.keywords.items(): if not required: return None - if isinstance(composite.value, KnownValue): - kwargs[kwarg] = composite.value.val - else: + kwarg_value = _extract_known_value(composite.value) + if kwarg_value is None: return None + kwargs[kwarg] = kwarg_value if actual_args.star_kwargs is not None: value = replace_known_sequence_value(actual_args.star_kwargs) if isinstance(value, DictIncompleteValue): for pair in value.kv_pairs: + if pair.is_many or not pair.is_required: + return None + key_val = _extract_known_value(pair.key) + value_val = _extract_known_value(pair.value) if ( - pair.is_required - and not pair.is_many - and isinstance(pair.key, KnownValue) - and isinstance(pair.key.val, str) - and isinstance(pair.value, KnownValue) + key_val is None + or value_val is None + or not isinstance(key_val.val, str) ): - kwargs[pair.key.val] = pair.value.val - else: return None + kwargs[key_val.val] = value_val.val else: return None @@ -2518,3 +2522,11 @@ def decompose_union( ), f"all union members matched between {expected_type} and {parent_value}" return bounds_map, union_used_any, unite_values(*remaining_values) return None + + +def _extract_known_value(val: Value) -> Optional[KnownValue]: + if isinstance(val, AnnotatedValue): + val = val.value + if isinstance(val, KnownValue): + return val + return None diff --git a/pyanalyze/test_signature.py b/pyanalyze/test_signature.py index 9978ec5a..8d483ea2 100644 --- a/pyanalyze/test_signature.py +++ b/pyanalyze/test_signature.py @@ -885,6 +885,17 @@ def capybara(): s.encode("not an encoding") # E: incompatible_call + @assert_passes() + def test_annotated_known(self): + from typing_extensions import Literal, Annotated + from pyanalyze.extensions import LiteralOnly + + def capybara(): + encoding: Annotated[Literal["ascii"], LiteralOnly()] = "ascii" + + s = "x" + assert_is_value(s.encode(encoding), KnownValue(b"x")) + class TestOverload(TestNameCheckVisitorBase): @assert_passes() From ac29e8cbac5ceb900e83d6941f1d091241b26376 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Thu, 25 Aug 2022 22:27:10 -0700 Subject: [PATCH 2/3] remove unused import --- pyanalyze/signature.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pyanalyze/signature.py b/pyanalyze/signature.py index a064e588..0ed3d2c6 100644 --- a/pyanalyze/signature.py +++ b/pyanalyze/signature.py @@ -37,7 +37,6 @@ from typing_extensions import assert_never, Literal, Protocol, Self from .error_code import ErrorCode -from .safe import all_of_type from .stacked_scopes import ( AbstractConstraint, AndConstraint, From 7dd83edda35a476b27dccf0d0ae12dfe0117ad12 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Thu, 25 Aug 2022 22:33:10 -0700 Subject: [PATCH 3/3] fix kwargs --- pyanalyze/signature.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyanalyze/signature.py b/pyanalyze/signature.py index 0ed3d2c6..2a7d91ca 100644 --- a/pyanalyze/signature.py +++ b/pyanalyze/signature.py @@ -1276,7 +1276,7 @@ def _maybe_perform_call( kwarg_value = _extract_known_value(composite.value) if kwarg_value is None: return None - kwargs[kwarg] = kwarg_value + kwargs[kwarg] = kwarg_value.val if actual_args.star_kwargs is not None: value = replace_known_sequence_value(actual_args.star_kwargs) if isinstance(value, DictIncompleteValue):