Skip to content

Commit 84d9dbf

Browse files
Make allow_call work with Annotated literals (#540)
1 parent ce578cb commit 84d9dbf

File tree

3 files changed

+43
-19
lines changed

3 files changed

+43
-19
lines changed

docs/changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Unreleased
44

5+
- `allow_call` callables are now also called if the arguments
6+
are literals wrapped in `Annotated` (#540)
57
- Support Python 3.11 (#537)
68
- Fix type checking of binary operators involving unions (#531)
79
- Improve `TypeVar` solution heuristic for constrained

pyanalyze/signature.py

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
from typing_extensions import assert_never, Literal, Protocol, Self
3838

3939
from .error_code import ErrorCode
40-
from .safe import all_of_type
4140
from .stacked_scopes import (
4241
AbstractConstraint,
4342
AndConstraint,
@@ -1254,41 +1253,45 @@ def _maybe_perform_call(
12541253
args = []
12551254
kwargs = {}
12561255
for definitely_present, composite in actual_args.positionals:
1257-
if definitely_present and isinstance(composite.value, KnownValue):
1258-
args.append(composite.value.val)
1259-
else:
1256+
if not definitely_present:
1257+
return None
1258+
arg = _extract_known_value(composite.value)
1259+
if arg is None:
12601260
return None
1261+
args.append(arg.val)
12611262
if actual_args.star_args is not None:
12621263
values = concrete_values_from_iterable(
12631264
actual_args.star_args, ctx.can_assign_ctx
12641265
)
1265-
if isinstance(values, collections.abc.Sequence) and all_of_type(
1266-
values, KnownValue
1267-
):
1268-
args += [val.val for val in values]
1269-
else:
1266+
if not isinstance(values, collections.abc.Sequence):
12701267
return None
1268+
for args_val in values:
1269+
arg = _extract_known_value(args_val)
1270+
if arg is None:
1271+
return None
1272+
args.append(arg.val)
12711273
for kwarg, (required, composite) in actual_args.keywords.items():
12721274
if not required:
12731275
return None
1274-
if isinstance(composite.value, KnownValue):
1275-
kwargs[kwarg] = composite.value.val
1276-
else:
1276+
kwarg_value = _extract_known_value(composite.value)
1277+
if kwarg_value is None:
12771278
return None
1279+
kwargs[kwarg] = kwarg_value.val
12781280
if actual_args.star_kwargs is not None:
12791281
value = replace_known_sequence_value(actual_args.star_kwargs)
12801282
if isinstance(value, DictIncompleteValue):
12811283
for pair in value.kv_pairs:
1284+
if pair.is_many or not pair.is_required:
1285+
return None
1286+
key_val = _extract_known_value(pair.key)
1287+
value_val = _extract_known_value(pair.value)
12821288
if (
1283-
pair.is_required
1284-
and not pair.is_many
1285-
and isinstance(pair.key, KnownValue)
1286-
and isinstance(pair.key.val, str)
1287-
and isinstance(pair.value, KnownValue)
1289+
key_val is None
1290+
or value_val is None
1291+
or not isinstance(key_val.val, str)
12881292
):
1289-
kwargs[pair.key.val] = pair.value.val
1290-
else:
12911293
return None
1294+
kwargs[key_val.val] = value_val.val
12921295
else:
12931296
return None
12941297

@@ -2518,3 +2521,11 @@ def decompose_union(
25182521
), f"all union members matched between {expected_type} and {parent_value}"
25192522
return bounds_map, union_used_any, unite_values(*remaining_values)
25202523
return None
2524+
2525+
2526+
def _extract_known_value(val: Value) -> Optional[KnownValue]:
2527+
if isinstance(val, AnnotatedValue):
2528+
val = val.value
2529+
if isinstance(val, KnownValue):
2530+
return val
2531+
return None

pyanalyze/test_signature.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,17 @@ def capybara():
885885

886886
s.encode("not an encoding") # E: incompatible_call
887887

888+
@assert_passes()
889+
def test_annotated_known(self):
890+
from typing_extensions import Literal, Annotated
891+
from pyanalyze.extensions import LiteralOnly
892+
893+
def capybara():
894+
encoding: Annotated[Literal["ascii"], LiteralOnly()] = "ascii"
895+
896+
s = "x"
897+
assert_is_value(s.encode(encoding), KnownValue(b"x"))
898+
888899

889900
class TestOverload(TestNameCheckVisitorBase):
890901
@assert_passes()

0 commit comments

Comments
 (0)