-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Keep Literal
after indexing?
#19152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Just for info, I circumvent this by proceeding in two steps: aligns: tuple[Literal["center", "right"], ...] = ("center", "right", "center")
aligns_no_extra = aligns[:no_extra] # Still `tuple[Literal["center", "right"], ...]` It would still be nice to have the possibility to proceed directly. |
I don't see why this isn't possible, unless we are using a heuristic on whether to use type context here (in which case this is just a sad consequence of that). |
I don't understand what you mean. I'm not sure whether you are saying that it's a possible feature to implement or saying that this should not raise the error. Just to be sure, I provide this MWE: from typing import Literal
foo = 0
bar: tuple[Literal[1], ...] = (1,)[foo:] # error: Incompatible types in assignment (expression has type "tuple[int, ...]", variable has type "tuple[Literal[1], ...]") [assignment]
baz: tuple[Literal[1], ...] = (1,)[0:] # OK
bar_fix: tuple[Literal[1], ...] = (1,) # In two steps...
bar_fix = bar_fix[foo:] # ...works fine |
This is trivially fixable, but I suspect this was intentionally not supported to prevent horror like this: reveal_type((1, 2, 3, 4, 5, 'foo', 'bar')[foo:]) # N: Revealed type is "builtins.tuple[Union[Literal[1]?, Literal[2]?, Literal[3]?, Literal[4]?, Literal[5]?, Literal['foo']?, Literal['bar']?], ...]" ...which is exactly what happens if we decide to retain literal values in tuples, and is bad enough IMO to not fix this corner case. If anyone has better ideas that retain literals only to some extent, here's what I did to receive that giant union: diff --git a/mypy/checker.py b/mypy/checker.py
index aceb02919..cb153a865 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -7261,14 +7261,15 @@ class TypeChecker(NodeVisitor[None], TypeCheckerSharedApi):
any_type = AnyType(TypeOfAny.from_omitted_generics)
return Instance(node, [any_type] * len(node.defn.type_vars))
- def named_generic_type(self, name: str, args: list[Type]) -> Instance:
+ def named_generic_type(self, name: str, args: list[Type], *, keep_last_known_values: bool = False) -> Instance:
"""Return an instance with the given name and type arguments.
Assume that the number of arguments is correct. Assume that
the name refers to a compatible generic type.
"""
info = self.lookup_typeinfo(name)
- args = [remove_instance_last_known_values(arg) for arg in args]
+ if not keep_last_known_values:
+ args = [remove_instance_last_known_values(arg) for arg in args]
# TODO: assert len(args) == len(info.defn.type_vars)
return Instance(info, args)
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index ec64669c1..4faad23fc 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -4617,7 +4617,7 @@ class ExpressionChecker(ExpressionVisitor[Type], ExpressionCheckerSharedApi):
# We could return the return type from above, but unions are often better than the join
union = self.union_tuple_fallback_item(left_type)
if isinstance(index, SliceExpr):
- return self.chk.named_generic_type("builtins.tuple", [union])
+ return self.chk.named_generic_type("builtins.tuple", [union], keep_last_known_values=True)
return union
def union_tuple_fallback_item(self, left_type: TupleType) -> Type:
diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test
index 3424d053f..a224ad7b0 100644
--- a/test-data/unit/check-tuples.test
+++ b/test-data/unit/check-tuples.test
@@ -1438,6 +1438,15 @@ reveal_type(t[x:]) # N: Revealed type is "builtins.tuple[Union[builtins.int, bu
t[y:] # E: Slice index must be an integer, SupportsIndex or None
[builtins fixtures/tuple.pyi]
+[case testNonLiteralSlicePreservesLiteralElements]
+from typing import Literal
+
+foo = 0
+bar: tuple[Literal[1], ...] = (1,)[foo:]
+baz: tuple[Literal[1], ...] = (1,)[0:]
+reveal_type((1,2,3,4,5,'foo','bar')[foo:])
+[builtins fixtures/tuple.pyi]
+
[case testTupleSliceStepZeroNoCrash]
# This was crashing: https://github.com/python/mypy/issues/18062
# TODO: emit better error when 0 is used for step |
Maybe return the type context ( |
Thanks for the details, it's interesting for me as What I mean is that your example indeed provides ugly revealed type, but maybe it should not unless the assignment explicitly types with Literals? Again, I'm not sure I'm making sense here. |
Uh oh!
There was an error while loading. Please reload this page.
EDIT: MWE
Original message
Hi there,
As always, I'm not sure whether I should post on
mypy
or pythontyping
page. The problem I encounter is the following.where
no_extra
is abool
. Essentially, I either keep or not the first element withmy_tuple[no_extra:]
, and this makes it into atuple[str, ...]
.Is it normal? If so, is there a recommended practice around it, or should this be a new feature?
Thanks in advance!
All the best.
Élie
The text was updated successfully, but these errors were encountered: