Open gu62jen opened 1 week ago
Same here with
def foo(a: date | Literal["latest"]) -> None:
...
Thanks for creating this issue. This does seem a bit counter intuitive from the type_enforced
side.
Note that Literal
is handled after type checking completes as a constraint. See Literal
in the docs here.
Given your definition, this means that first type_enforced checks to see that the passed item is an int and raises an error if it is not. Then if it is an int, the constraint a in ["bar"]
is checked. This is a unique aspect of Literal / Constraints.
Now this leaves a conundrum in this case because even if you specified:
@type_enforced.Enforcer
def foo(a: Literal["bar"] | int | str) -> None:
pass
This would still fail on the Literal check if an int
was passed.
Ideally, the Literal check would happen as an option during the checking stage, but this adds some complexity and time as well as other implicaitons.
For now, you could consider using a custom constraint to handle this use case although it does arguably look a bit ugly. @JSchoeck this should probably allow for your date validation with some modification as well.
import type_enforced
from typing import Literal
from type_enforced.utils import GenericConstraint
bar_or_int = GenericConstraint({
'int_or_str': lambda x: isinstance(x, (int, str)),
'bar_if_str': lambda x: x == 'bar' if isinstance(x, str) else True,
})
@type_enforced.Enforcer
def foo(a: bar_or_int) -> None:
pass
foo("baz") # Passes
foo(1) # Passes
foo("baz") # TypeError: (foo): Constraint validation error for variable `a`. Constraint `bar_if_str` not met with the provided value `baz`
I want to mull this over a bit before deciding on a path forward on handling Literals. Any ideas / suggestions are very welcome.
From what I have seen in the docs, Literal and | should both be supported as type hints. However, combined they always give me a type error.