-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Add initial LiteralString
support
#13664
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
base: master
Are you sure you want to change the base?
Conversation
Current failures show that we need to special case this: x = 'a'
x = 'Value: {}'.format(x) |
This comment has been minimized.
This comment has been minimized.
|
||
expects_literal_string('a') | ||
expects_literal_string('a' + 'b') | ||
expects_literal_string('a' * 2) |
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.
Why do we need to care about this at all? Isn't this just a question of defining these correctly in typeshed? For example:
@overload
def __add__(self: LiteralStr, other: LiteralStr) -> LiteralStr: ...
@overload
def __add__(self, other: str) -> str: ...
It seems to be much easier to agree on this once and for all rather than special-casing all these in all type-checkers.
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.
Good question! I think that we should have both.
Typeshed will store the base implementation for it.
But, mypy can go further with full literal type math.
Or we can just use the typeshed if ltm is never merged / implemented.
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.
OK, I see, but as I said below, we need to be sure "typeshed way" will work well. Having "a" + "b"
accepted where Literal["ab"]
is expected is lower priority IMO.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I consider the very first version done! 🎉 |
This comment has been minimized.
This comment has been minimized.
Primer output
|
This comment has been minimized.
This comment has been minimized.
1 similar comment
This comment has been minimized.
This comment has been minimized.
Currently we patch typeshed... Can we revert #13093 in this PR and see if everything works? |
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.
Thanks for working on this! This is an important feature to have now that 3.11 will be soon out. Not a full review .
T = TypeVar('T', bound=LiteralString) | ||
def expects_literal_string(x: T): ... | ||
|
||
expects_literal_string('a') |
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.
Maybe also test something like this:
ls: Literal['a'] = expects_literal_string('a')
@overload | ||
def some(x: LiteralString, y: LiteralString) -> LiteralString: ... | ||
@overload | ||
def some(x: str, y: str) -> str: ... |
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 also a str
method with self
annotated as self: LiteralString
in one overload variant (e.g. for __add__
)? This way we should be able to make 'x' + 'y'
be a LiteralString without special casing?
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.
I actually tried this and mypy gives an error like Self type is not a supertype of class
. I think this is too strict, we already check self-type at call site. I would say we can simply skip this check for overloads (maybe only keep it for overload implementation).
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.
Just to be clear, I think we still need this test, see also my comment above #13664 (comment)
@@ -0,0 +1,57 @@ | |||
# Builtins stub used in tuple-related test cases. |
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.
The comment is out of date.
+1 to this idea. |
I am away for a week, I will return to this shortly after I get home! Thanks for your feedback. |
Adding this back to my todo list :) |
Diff from mypy_primer, showing the effect of this PR on open source code: sphinx (https://github.com/sphinx-doc/sphinx)
+ sphinx/setup_command.py:133:43: error: Argument 1 to "join" has incompatible type "str"; expected "LiteralString" [arg-type]
psycopg (https://github.com/psycopg/psycopg)
+ psycopg/psycopg/_typeinfo.py:93: error: Argument 1 to "execute" of "Cursor" has incompatible type "str"; expected "Union[LiteralString, bytes, SQL, Composed]" [arg-type]
+ psycopg/psycopg/_typeinfo.py:112: error: Argument 1 to "execute" of "AsyncCursor" has incompatible type "str"; expected "Union[LiteralString, bytes, SQL, Composed]" [arg-type]
+ psycopg/psycopg/server_cursor.py:170: error: Argument 1 to "SQL" has incompatible type "str"; expected "LiteralString" [arg-type]
+ psycopg/psycopg/server_cursor.py:179: error: Incompatible types in assignment (expression has type "str", variable has type "Union[LiteralString, bytes, SQL, Composed]") [assignment]
+ psycopg/psycopg/server_cursor.py:181: error: Argument 1 to "SQL" has incompatible type "Union[LiteralString, bytes]"; expected "LiteralString" [arg-type]
+ psycopg/psycopg/server_cursor.py:188: error: Argument 1 to "SQL" has incompatible type "str"; expected "LiteralString" [arg-type]
+ psycopg/psycopg/connection.py:1024: error: Argument 1 to "execute" of "Cursor" has incompatible type "str"; expected "Union[LiteralString, bytes, SQL, Composed]" [arg-type]
+ psycopg/psycopg/connection_async.py:430: error: Argument 1 to "execute" of "AsyncCursor" has incompatible type "str"; expected "Union[LiteralString, bytes, SQL, Composed]" [arg-type]
+ tests/scripts/dectest.py:31: error: Argument 1 to "copy" of "Cursor" has incompatible type "str"; expected "Union[LiteralString, bytes, SQL, Composed]" [arg-type]
+ tests/scripts/dectest.py:40: error: Argument 1 to "executemany" of "Cursor" has incompatible type "str"; expected "Union[LiteralString, bytes, SQL, Composed]" [arg-type]
+ tests/crdb/test_copy.py:209: error: Argument 1 to "SQL" has incompatible type "str"; expected "LiteralString" [arg-type]
+ tests/crdb/test_copy_async.py:215: error: Argument 1 to "SQL" has incompatible type "str"; expected "LiteralString" [arg-type]
+ tests/test_sql.py:466: error: Argument 1 to "SQL" has incompatible type "str"; expected "LiteralString" [arg-type]
+ tests/test_sql.py:534: error: Argument 1 to "SQL" has incompatible type "str"; expected "LiteralString" [arg-type]
+ tests/types/test_numeric.py:580: error: Argument 1 to "SQL" has incompatible type "str"; expected "LiteralString" [arg-type]
spark (https://github.com/apache/spark)
+ python/pyspark/pandas/sql_formatter.py:194: error: Signature of "vformat" incompatible with supertype "Formatter" [override]
+ python/pyspark/pandas/sql_formatter.py:194: note: Superclass:
+ python/pyspark/pandas/sql_formatter.py:194: note: @overload
+ python/pyspark/pandas/sql_formatter.py:194: note: def vformat(self, format_string: LiteralString, args: Sequence[LiteralString], kwargs: Mapping[LiteralString, LiteralString]) -> LiteralString
+ python/pyspark/pandas/sql_formatter.py:194: note: @overload
+ python/pyspark/pandas/sql_formatter.py:194: note: def vformat(self, format_string: str, args: Sequence[Any], kwargs: Mapping[str, Any]) -> str
+ python/pyspark/pandas/sql_formatter.py:194: note: Subclass:
+ python/pyspark/pandas/sql_formatter.py:194: note: def vformat(self, format_string: str, args: Sequence[Any], kwargs: Mapping[str, Any]) -> str
|
Working on it! |
@sobolevn It looks like you may have forgotten about this one :-) It would be great to finally finish this. |
Yes, I am sorry :( I will try to finish this next week! |
I went for the smallest possible diff in this feature.
There are couple of other options I did not want to go with:
class LiteralStringType
and using it inlast_known_value
. There are lots of places where.last_known_value
is used for literal context checking. In this case we would also need to add new visitor methods to every type visitor, which is way harderLiteralString(str)
type infoLiteralString
with_promote
hackeryTODO in the next PRs:
x: LiteralString = 'a' + 'b'
--enable-incomplete-features
flag from this featureRefs #12554