-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Add support for literal addition #18004
base: master
Are you sure you want to change the base?
Conversation
This comment has been minimized.
This comment has been minimized.
Can you look at the new errors reported by mypy_primer? If we infer literal types more eagerly, they could generate new false positives. |
The primer results look quite promising IMO. For from typing import Literal
class SessionMiddleware:
def __init__(
self,
same_site: Literal["lax", "strict", "none"] = "lax",
https_only: bool = False,
) -> None:
...
self.security_flags = "httponly; samesite=" + same_site
if https_only: # Secure flag can be used with HTTPS only
self.security_flags += "; secure" # <-- error It can be resolved by annotation -- class CaptureMethodBase:
description = ""
class BitBltCaptureMethod(CaptureMethodBase):
description = (
"\nThe best option when compatible. But it cannot properly record "
+ "\nOpenGL, Hardware Accelerated or Exclusive Fullscreen windows. "
+ "\nThe smaller the selected region, the more efficient it is. "
)
class ForceFullContentRenderingCaptureMethod(BitBltCaptureMethod):
description = (
"\nUses BitBlt behind the scene, but passes a special flag "
+ "\nto PrintWindow to force rendering the entire desktop. "
+ "\nAbout 10-15x slower than BitBlt based on original window size "
+ "\nand can mess up some applications' rendering pipelines. "
) Just removing the -- Not completely sure whats going on with |
I think implicit and explicit string concatenation should behave the same. Otherwise it would seem inconsistent. I also know that some people prefer explicit string concatenation as a style thing, since it's easy to accidentally use implicit string concatenation (e.g. omit a comma at the end). |
What about inferring a literal addition only if one of the operands is not a literal expression? We have tried to adhere to an informal policy where literal types are not inferred for variables "out of thin air", i.e. an explicit |
In particular, this feels inconsistent to me: class A:
a = ""
class B(A):
a = "a" + "b"
class C:
a = "a" + "b"
reveal_type(A.a) # Revealed type is "builtins.str"
reveal_type(B.a) # Revealed type is "Literal['ab']" (why literal here?)
reveal_type(C.a) # Revealed type is "builtins.str" |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Makes sense. I missed the |
Diff from mypy_primer, showing the effect of this PR on open source code: starlette (https://github.com/encode/starlette)
+ starlette/middleware/sessions.py:34: error: Incompatible types in assignment (expression has type "Literal['httponly; samesite=lax; secure', 'httponly; samesite=none; secure', 'httponly; samesite=strict; secure']", variable has type "Literal['httponly; samesite=lax', 'httponly; samesite=none', 'httponly; samesite=strict']") [assignment]
+ starlette/middleware/sessions.py:36: error: Incompatible types in assignment (expression has type "str", variable has type "Literal['httponly; samesite=lax', 'httponly; samesite=none', 'httponly; samesite=strict']") [assignment]
|
Related issue: #11616 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did a quick pass, mainly to look at the big picture. I can also test this manually later.
if isinstance(expr, BytesExpr): | ||
return [expr.value], "builtins.bytes", False | ||
|
||
typ = typ or self.accept(expr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calling accept
here can be inefficient, since the caller may also call accept
on the expression at some point, causing duplicate work. This could cause exponential time complexity in nested expression trees (we already have examples of this and it can cause extremely slow type checking). Maybe the caller can pass the expression type, so that we only call accept
once per expression? Maybe the new logic should be called from check_op
, when we have the right operand type?
|
||
values: list[int | str] = sorted( | ||
{ | ||
val[0] + val[1] # type: ignore[operator] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you avoid the type: ignore
here? In the future we will probably disallow many ignores in code compiled with mypyc, since some type ignores can cause erratic behavior, such as C compilation errors.
@@ -2960,3 +2960,90 @@ class C(B[Literal["word"]]): | |||
reveal_type(C().collection) # N: Revealed type is "builtins.list[Literal['word']]" | |||
reveal_type(C().word) # N: Revealed type is "Literal['word']" | |||
[builtins fixtures/tuple.pyi] | |||
|
|||
[case testLiteralAddition] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add the interesting bits of the inheritance example I gave in an earlier comment?
reveal_type(d + 'foo') # N: Revealed type is "builtins.str" | ||
reveal_type('foo' + d) # N: Revealed type is "builtins.str" | ||
reveal_type(d + 'bar') # N: Revealed type is "Literal['foobar']" | ||
reveal_type('bar' + d) # N: Revealed type is "Literal['barfoo']" | ||
|
||
reveal_type(a.__add__(b)) # N: Revealed type is "builtins.int" | ||
reveal_type(b.__add__(a)) # N: Revealed type is "builtins.int" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check a.__add__(a)
. It would be nice if it was consistent with a + a
, though it's probably not important.
bytes_a: Literal[b"a"] | ||
bytes_b: Literal[b"b"] | ||
bytes_union_1: Literal[b"a", b"b"] | ||
bytes_union_2: Literal[b"d", b"c"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test union with literal type and non-literal type -- e.g. user-defined class that defines __add__
that accepts str
. Also test with union of literal type and Any
.
Add support for addition of Literal values and str, int and bytes expressions. This is especially useful when working with TypedDicts. Previously this code would have emitted a
literal-required
error.Refs #11616