I’m not sure what you’re proposing. Could you go into more detail about what you mean by “the special case of one-argument constrained type variables”? Do you mean “a callable whose input signature uses a function-scoped constrained type variable only once”?
I think the underlying problem here is that the definition for the parse_qsl
function in the typeshed stubs is incorrect. It should use an overload, but it instead (mis)uses a type variable with value constraints. This was probably done for brevity — or because the developer who wrote the definition didn’t understand the implications of using a constrained type variable in this case.
Currently, this function has the following definition in typeshed.
def parse_qsl(
qs: AnyStr | None,
... <additional params omitted>
) -> list[tuple[AnyStr, AnyStr]]: ...
In addition to the problem caused by the (mis)use of the value constraints, this definition also has the problem that the type variable can go unsolved if the user passes None
to the qs
parameter. I’d need to dig into the implementation of this function to understand what it actually returns in that case, but it’s probably not what the definition currently indicates.
The definition should probably be changed to something like this:
@overload
def parse_qsl(
qs: None,
... <additional params omitted>
) -> ????????: ...
@overload
def parse_qsl(
qs: str,
... <additional params omitted>
) -> list[tuple[str, str]]: ...
@overload
def parse_qsl(
qs: bytes,
... <additional params omitted>
) -> list[tuple[bytes, bytes]]: ...
I’ve confirmed that with this modified definition, your code sample type checks without a problem.
If that sounds like a good solution, I recommend filing a bug report and/or a PR in the typeshed project.
There’s a need for additional coverage in the typing spec about how type variables with value constraints should work. PEP 484 was very light on details here, but there are rules that type checkers follow. Making those rules explicit in the typing spec is on the to-do list. These rules are already complex, so creating special cases on top of the existing rules is probably not the right answer. Special cases inevitably lead to composability problems.
I think there’s also need for better developer guidance about when value constraints should and shouldn’t be used. I find that developers often reach for them in cases where they should not. This is a good example.
Maybe type checkers should detect and report situations where a type variable with value constraints is inappropriately used within a function definition, like it is in the case of parse_qsl
. We’d need to think about whether we could establish rules that wouldn’t lead to a bunch of false positives.