Description
[This is based on a discussion between me and @gvanrossum last week. I'm writing this down so that we won't forget about this.]
Several stdlib functions have signatures where the return type depends on the value of an argument. Examples:
open
returns anIO[str]
orIO[bytes]
depending on whether the mode argument has'b'
as a substring.subprocess.check_output
returns astr
orbytes
depending on the value of the booleanuniversal_newlines
argument.
A simple way to support these would be to introduce a few additional types that work with str
and bool
literals:
- A string pattern type that can describe the contents of a string literal, perhaps using a regular expression.
- Types for
False
andTrue
.
For example, if we had FalseType
and TrueType
, we could write a function whose return value depends on the value of a boolean argument:
@overload
def f(x: FalseType) -> bytes: ...
@overload
def f(x: TrueType) -> str: ...
def f(x: Any) -> Any:
if x:
return 'x'
else:
return b'x'
reveal_type(f(False)) # bytes
reveal_type(f(True) # str
Not sure about this one:
...
x = bool('')
reveal_type(f(x)) # Union[str, bytes] since we don't know the boolean value statically?
We could also have a generic class where a generic type argument depends on the value of an argument to __init__
. Not sure how we should represent these -- here is one potential approach:
class C(Generic[AnyStr]):
@overload
@returntype('C[str]')
def __init__(self, x: TrueType) -> None: ...
@overload
@returntype('C[bytes]')
def __init__(self, x: FalseType) -> None: ...
...
reveal_type(C(True)) # C[str]
Alternatively, we could use __new__
, but this would be problematic if there is no corresponding method defined at runtime.