-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Recognizing expressions with a constant value. #12583
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
There is some overlap between this proposal and the "literal math" proposal, although the latter doesn't require running arbitrary code during analysis time. I won't speak for the maintainers of mypy, but I think it seems like executing arbitrary code as part of type analysis would be ill advised — both for safety and performance reasons. Mypy is, after all, a static analyzer. There are other tools that perform runtime type validation, but that's outside the scope of mypy. |
I would like to add another use case for this; however, as @erictraut suggests, this might be outside mypy scope. In psycopg we are moving to auto-generate sync code from async code. We do this with AST transformation. There are snippets of code that we just don't want on the sync side or on the async side, are just not valid on the sync side or on the async side, so we guard them with if True: # ASYNC
import sys
import asyncio
from asyncio import Lock
else:
from threading import Lock
...
if True: # ASYNC
if sys.platform == "win32":
loop = asyncio.get_running_loop()
if isinstance(loop, asyncio.ProactorEventLoop):
raise e.InterfaceError(...) Both Python and MyPy are able to prune the unneeded branch, while the transformation script will prune the async-side of the if in order to delete code that might be invalid in the sync side. This works, however is relatively ugly, and requires the use of a third party project such as ast-comments in order to retain the comment in the ast (we would probably use this project anyway to retain the comment in the generated sync output). I tried to replace the pattern using For instance, this code shows an error (in mypy 1.5.1), but no error if the test is replaced by async def f() -> None:
pass
async def g() -> None:
if "async":
await f()
else:
f() It would be great if the else-side branch of |
Feature
Determining the actual value of a variable (or an expression) if it can be determined to be constant.
Pitch
mypy tries to determine the type of an expression already, and if it involves typed functions or known operators using values of known type, then it can infer the type of this function or operation. And so on.
There are some cases in which it would be useful for mypy to determine the actual value, not just the type, when the value can be determined statically.
should pass type checking. Better yet, the inferred type of
var
could beLiteral[7]
instead ofint
.Even if
var
is not Final, mypy would recognize that its value is 7, until possibly reassigned later in the same scope.I think that mypy could evaluate an arbitrary expression safely by compiling it and executing the compiled code, under limited conditions. All names in the expression must have a known value, or be builtin functions. Thingsl ike
n += 1
inside a loop can't be evaluated. No user-defined functions or classes. Any value returned from the compiled code (if no exception is raised) would be deemed to be the fixed value.The known value could include tuples, lists, etc. You might ask why these would be useful, since they cannot be used in a Literal type. However, such a collection might be subscripted, as in
['a', 3][1]
having a constant value of 3 which can match a Literal[3] type.More important is the value of
__all__
when it is not an actual list or tuple expression. The user could have some value like"a b".split()
, orlist1 + list2
; or have a statement like__all__.append("c")
. Generally, if__all__
has a value at the end of the module code that is some fixedIterable[str]
then mypy should use this to determine the exported names from the module. This would be a solution for #12582, and I won't have to convert mysplit()
s to lists in order to pass type checking.As a speedup, the analysis of fixed value of a variable could be postponed until the variable is actually used where a Literal type is expected, or if the module is used in
import *
in a different module. In the latter case, the names mentioned in__all__
are in the globals already, but the public/private status of each global name only matters for purposes ofimport *
.The text was updated successfully, but these errors were encountered: