Yes, I agree with the outcomes. What seems problematic to me about this is that static and dynamic type checking become mutually exclusive; the f(x+1) example. I'm not conversant enough with static type checking practices to know: how should one pass a value to a Literal when the value being passed is not static? For example, if mode were added to open(..., mode: Literal["r", "w", "r+", "w+", ...]) and mode were dynamically generated, how would one express it so as to pass muster with mypy? On Sat, 2021-09-18 at 07:34 -0700, Guido van Rossum wrote:
Hi Paul,
I cannot follow your proposal (or complaint?) without an example,
Here's how I suppose it could work at runtime.
def g(a: Literal[1, 2, 3]): ...
def f(a: Literal[1, 2]): g(a)
x: Literal[1] = 1 # Could be a value read from a JSON file
f(x) # Passes both static and runtime checks f(x+1) # Passes at runtime, fails static check f(x+2) # Fails both checks
Do you agree with these outcomes? (I don't have Pydantic handy, so I don't actually know what it does, but based on your description it should check the value of x, x+1 and x+2 against Literal[1, 2].) If not, what would you want? If yes, can you show an example of what you are talking about?
--Guido
On Sat, Sep 18, 2021 at 12:00 AM Paul Bryan <pbryan@anode.ca> wrote:
Background
typing.Literal was defined with static type checking in mind. During static type checking, Literal requires the value being checked to be a literal value, matching one of the Literal value(s) specified. In a static type checking context, this behaviour is both intuitive and obvious.
Problem
During dynamic type checking, Literal can be specified in a type hint, but there is no obvious way to determine at runtime whether the value being checked is sourced from a Python literal or elsewhere. For example, it could have been loaded from JSON. Furthermore, it is not necessarily desirable to make such a determination at runtime.
Pydantic currently validates a value against a Literal type hint without regard to whether it is a Python literal. It appears to simply check for type and value equality against the defined Literal values in the type hint. (This is originally how I interpreted Literal would behave when I first encountered it.) I believe this makes Pydantic’s use of Literal incompatible with static type checking tools like mypy.
Ideas
1. Codify Pydantic’s current behavior without changing static type checking behavior (in PEP 586 or elsewhere) This would lead to many situations where code would pass type checking at runtime, but fail static type checking due to constraints on literal values.
2. Relax Literal’s “literalness” static type checking behavior I believe this would conflict with developers’ objectives of keeping the value “safe” by requiring it be literally supplied, not allowing it to be deserialized or built dynamically. (Discussed previously on this list.)
3. Just use Enum I would argue Enum is unnecessary verbose, requiring the definition of a class, separate members, subclassing and dunders (e.g. __str__) to handle string values. It requires unnecessary naming of values, and requires avoiding “invalid” names. (3.11’s StrEnum could mitigate some of this?)
4. Define a new “relaxed” Literal type hint Define a new type hint, paralleling Literal’s syntax, relaxing the requirement that the value be an actual literal. Candidate names like Figure[...], Value[...], enum[...]. It would likely not be included in stdlib, so would likely be implemented by dynamic type checking libraries.
What are your thoughts? Thanks in advance for your consideration.
_______________________________________________ Typing-sig mailing list -- typing-sig@python.org To unsubscribe send an email to typing-sig-leave@python.org https://mail.python.org/mailman3/lists/typing-sig.python.org/ Member address: guido@python.org