Support for `ParamSpec` for `type`

Open
#1,991 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
30/100
Issue type
Feature
Clarity
Needs clarification
Activity status
Stale
Tech stack
python
Domain
tooling

Research direction

Review the ParamSpec behavior for type and Callable described in the examples, then compare it with the possible direction in typing issue #1966. Done would require an agreed representation that preserves constructor parameter information and makes the proposed overload distinguishable, but this issue does not name implementation files or tests.

Written by the indexing model from the issue text.

Description

topic: feature

The problem I'm having is that the type doesn't support ParamSpec, so I can't use that to enforce args and kwargs, in the same way as as Callable. I'm not 100% sure whether this is correct, but isn't every type a Callable, so in a sense that type is a subtype of Callable, so there should be a way to represent type in the same way as a Callable to obey Liskov?

For example, I'm trying to write an overload such that passing in a type will return ExpectType, while a general Callable returns ExpectCallable:

from collections.abc import Callable

import typing_extensions as t

P = t.ParamSpec("P")
T_co = t.TypeVar("T_co", covariant=True)

class ExpectCallable(t.Generic[P, T_co]): ...
class ExpectType(ExpectCallable[P, T_co]): ...

@t.overload
def expect(
    v: type[T_co], /, *args: P.args, **kwargs: P.kwargs
) -> ExpectType[P, T_co]: ...
@t.overload
def expect(
    v: Callable[P, T_co], /, *args: P.args, **kwargs: P.kwargs
) -> ExpectCallable[P, T_co]: ...
def expect(
    v: type[T_co] | Callable[P, T_co], /, *args: P.args, **kwargs: P.kwargs
) -> ExpectType[P, T_co] | ExpectCallable[P, T_co]:
    return ExpectType() if isinstance(v, type) else ExpectCallable()

class A:
    def __init__(self, inp: str, /) -> None: ...

def fn(inp: str, /) -> None: ...

t.assert_type(expect(A), ExpectType[[], A])  # Shouldn't be allowed  
t.assert_type(expect(fn, "inp"), ExpectCallable[[str], None])

As you can see that t.assert_type(expect(A), ExpectType[[], A]) passes with no errors, even though A is suppose to take in a str argument. I'm expecting expect(A) to require a str argument, similar to how expect(fn, "inp") requires the second parameter "inp".

Note that I haven't used *args and **kwargs in the example for simplicity, but they are meant to be used inside of the Expect classes.

Maybe the simplest way is to add a new class TypeCallable that allows for TypeCallable[P, T] instead of modifying type, so the above example is achievable:

@t.overload
def expect(
    v: TypeCallable[P, T_co], /, *args: P.args, **kwargs: P.kwargs
) -> ExpectType[P, T_co]: ...
@t.overload
def expect(
    v: Callable[P, T_co], /, *args: P.args, **kwargs: P.kwargs
) -> ExpectCallable[P, T_co]: ...
def expect(
    v: TypeCallable[P, T_co] | Callable[P, T_co], /, *args: P.args, **kwargs: P.kwargs
) -> ExpectType[P, T_co] | ExpectCallable[P, T_co]:
    return ExpectType() if isinstance(v, type) else ExpectCallable()

Note that I think https://github.com/python/typing/issues/1966 might also solve the above problem.

Dominant language
Python
Stars
1.8k
Forks
302
Avg merge
23h
Merged PRs (30d)
8

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from python/typing

All issues in python/typing

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.