Skip to content

re.Pattern, re.Match etc. should not be callable #14558

Closed as not planned
Closed as not planned

Description

@dchevell

The following invalid code type checks in mypy and pyright:

# test.py
import re

obj1 = re.Pattern()
$ mypy test.py
Success: no issues found in 1 source file

$ mypy --version
mypy 1.17.1 (compiled: yes)

$ pyright test.py
0 errors, 0 warnings, 0 informations

$ pyright --version
pyright 1.1.403

At runtime, this would fail with TypeError: cannot create 're.Pattern' instances.

As a test, I modified the typeshed stub for Pattern to inherit from Protocol so the lack of a __call__ definition would be caught. No doubt this is a naive approach (nothing is ever so simple), but it seems to work:

- class Pattern(Generic[AnyStr]):
+ class Pattern(Generic[AnyStr], Protocol):
$ mypy test.py
test.py:3: error: Cannot instantiate protocol class "Pattern"  [misc]
Found 1 error in 1 file (checked 1 source file)

$ pyright test.py
/path/to/test.py
  /path/to/test.py:3:1 - error: Cannot instantiate Protocol class "Pattern" (reportAbstractUsage)
1 error, 0 warnings, 0 informations

Aside from re.Pattern and re.Match I'm not sure if there are other types in the stdlib that are not callable but also not ABC's, so this may be a special case.

Activity

  1. JelleZijlstra commented on Aug 11, 2025

    @JelleZijlstra
    Member

    This is true for quite a few C-implemented classes, for example (in the types module):

    >>> for k, v in types.__dict__.items():
    ...     if isinstance(v, type):
    ...         try: obj = v()
    ...         except Exception as e:
    ...             if "cannot create" in str(e): print(f"can't instantiate {k} = {v}: {e}")
    ...             
    can't instantiate AsyncGeneratorType = <class 'async_generator'>: cannot create 'async_generator' instances
    can't instantiate BuiltinFunctionType = <class 'builtin_function_or_method'>: cannot create 'builtin_function_or_method' instances
    can't instantiate BuiltinMethodType = <class 'builtin_function_or_method'>: cannot create 'builtin_function_or_method' instances
    can't instantiate CapsuleType = <class 'PyCapsule'>: cannot create 'PyCapsule' instances
    can't instantiate ClassMethodDescriptorType = <class 'classmethod_descriptor'>: cannot create 'classmethod_descriptor' instances
    can't instantiate CoroutineType = <class 'coroutine'>: cannot create 'coroutine' instances
    can't instantiate FrameType = <class 'frame'>: cannot create 'frame' instances
    can't instantiate GeneratorType = <class 'generator'>: cannot create 'generator' instances
    can't instantiate GetSetDescriptorType = <class 'getset_descriptor'>: cannot create 'getset_descriptor' instances
    can't instantiate MemberDescriptorType = <class 'member_descriptor'>: cannot create 'member_descriptor' instances
    can't instantiate MethodDescriptorType = <class 'method_descriptor'>: cannot create 'method_descriptor' instances
    can't instantiate MethodWrapperType = <class 'method-wrapper'>: cannot create 'method-wrapper' instances
    can't instantiate UnionType = <class 'typing.Union'>: cannot create 'typing.Union' instances
    can't instantiate WrapperDescriptorType = <class 'wrapper_descriptor'>: cannot create 'wrapper_descriptor' instances
    

    Marking these as Protocols would be quite confusing; they aren't protocols, and marking them as such is likely to lead to worse confusion.

    I don't think there's currently a way to mark a class as not instantiable that works across type checkers. If this is important to you, it's a reasonable small feature to add. Maybe it could work by setting __new__ = None.

  2. dchevell commented on Aug 11, 2025

    @dchevell
    Author

    Good point, I forgot types. Agreed they aren't Protocols either, I'm just not well versed enough in the type system to know the "correct" way to type this.

    Weirdly enough, I found a mypy issue (python/mypy#10342 / ) + PR (python/mypy#13480) that seems to suggest a fix was implemented where one can annotate __new__ or __init__ with NoReturn … but in my testing it doesn't seem to work. Even if it did, though, I'm not sure if this is part of the typing spec (without which a change in typeshed doesn't seem warranted).

    On further consideration I think the deeper issue is that type is assignable to Callable, so even if explicit usage of these types could theoretically be fixed it still wouldn't work in the context of generics where type[T] is considered assignable to Callable[..., T]. This is probably a narrow use case (though perhaps libraries like trycast & typedload might run into it) and whilst it may be more technically correct not to assume type is always callable, in practice I can imagine it breaking more stuff out there than not. Maybe a future type negation feature, combined with a fix for these types if one is possible?

  3. added
    reason: inexpressibleClosed, because this can't be expressed within the current type system
    on Aug 11, 2025
  4. srittau commented on Aug 11, 2025

    @srittau
    Collaborator

    I've opened a new typing feature issue about this: python/typing#2066. But as long as this hasn't been resolved, I'm going to close this as inexpressible for now (although I think this would be a good feature to have).

  5. JukkaL commented on Aug 15, 2025

    @JukkaL
    Contributor

    Would it be practical to make these abstract (i.e. ABCs)?

  6. JelleZijlstra commented on Aug 16, 2025

    @JelleZijlstra
    Member

    They aren't abstract though, and they aren't subclasses of ABC at runtime. We could pretend they are in the stubs, but there's a good chance that will cause new problems.

  7. dchevell commented on Aug 16, 2025

    @dchevell
    Author

    I solved my problem with a callback protocol that requires at least one positional parameter - it does make me think the best answer is a way to annotate a call with something that indicates it’s not supposed to be called, because technically it has a __call__ method … It just happens to raise an exception at runtime. I return type of Never might fit better with methods that do exist but raise runtime exceptions, and seems like the more natural way for and use code to define classes which shouldn’t be directly instantiated

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    reason: inexpressibleClosed, because this can't be expressed within the current type system

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions