Open xuhdev opened 3 years ago
I'm not sure this is a bug: the PEP notes that "", and mypy
is perfectly happy if you omit the Final
:
from typing import Literal
var = Literal[Literal[1]] = 1
And in Literal[s]
, s
is a name, not a literal value.
while this may be not a bug, I thik this design decision limits the usefulness of Literal
s - and Final
s
let's say I define a pair of constants
from typing import Final, Literal
MY_CONST_A: Final = 1
MY_CONST_B: Final = 2
from now on I would like to avoid using directly the values 1
and 2
and instead refer to the constants MY_CONST_A
and MY_CONST_B
.
let's say I want to define a function that can return either of that consts, what the return type should be? or similarly, I want to set the type of a value that can be set to one of the two values; what is it's type?
def foo() -> Literal[MY_CONST_A, MY_CONST_B]:
return MY_CONST_A if ... else MY_CONST_B
is not supported by mypy, and I need to set Literal[1,2]
as return type to make the signature work
This proposal would require an update to PEP 586, so this discussion should probably be moved to the typing-sig forum rather than mypy's issue tracker. (For full transparency, I'm one of the primary contributors to pyright, Microsoft's Python type checker.)
If we were to adopt a change like this, I think we'd need to place some additional constraints on the constants. Not only would they need to be marked Final
, but they'd also need to be assigned an expression that is unambiguously a literal value — in other words, expressions that are allowed today within a Literal
. That excludes more complex expressions.
For example, I would propose that none of these constants should be allowed in a Literal
.
MY_CONST_C: Final = my_func()
MY_CONST_D: Final = 1 + 2
MY_CONST_E: Final = 1 if a else 2
MY_CONST_F: Final = (1, 2, 3)[0]
MY_CONST_G: Final = ...
The problem with more complex expressions is that different type checkers can infer different types. If we constrain it to literal values only, there's no ambiguity or inconsistency between type checkers.
I'm not sure if it's helpful or not, but PEP 586 already mentions this:
Literal may also be parameterized by other literal types, or type aliases to other literal types. For example, the following is legal:
ReadOnlyMode = Literal["r", "r+"] WriteAndTruncateMode = Literal["w", "w+", "wt", "w+t"] WriteNoTruncateMode = Literal["r+", "r+t"] AppendMode = Literal["a", "a+", "at", "a+t"] AllModes = Literal[ReadOnlyMode, WriteAndTruncateMode, WriteNoTruncateMode, AppendMode]
This feature is again intended to help make using and reusing literal types more ergonomic.
That being said, I just ran into this issue while trying to specify a variable type as Literal[logging.NOTSET, logging.DEBUG]
, caused most likely by those being inferred by MyPy as int
rather than Literal[0]
and Literal[10]
respectively. Regarding the type being int
instead of Literal
, I've proposed a typeshed PR: https://github.com/python/typeshed/pull/6610
Having that PR accepted, MyPy would still be expected to accept Literal
type aliases like so:
NOTSET: Literal[0]
DEBUG: Literal[10]
# currently valid
logging_level: Literal[Literal[0], Literal[10]]
# currently invalid
logging_level: Literal[NOTSET, DEBUG]
What if we take a different approach, and the Type parameterization is extended with Literals? I.e. if we could get this working?
MY_CONST_A: Final = 1
MY_CONST_B: Final = 2
def foo() -> Type[MY_CONST_A] | Type[MY_CONST_B]:
return MY_CONST_A if ... else MY_CONST_B
Feature
Currently
Literal
doesn't accept anotherLiteral
orFinal[Literal]
. For example:outputs:
Pitch
This is particularly useful when there are some defined constants and people refer to those constants rather than the actual value. For example, some of these socket constants: https://docs.python.org/3/library/socket.html#socket.BDADDR_ANY