re.Pattern, re.Match etc. should not be callable #14558
Copy link
Copy link
Closed as not planned
Labels
reason: inexpressibleClosed, because this can't be expressed within the current type systemClosed, because this can't be expressed within the current type system
Description
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.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
reason: inexpressibleClosed, because this can't be expressed within the current type systemClosed, because this can't be expressed within the current type system
Activity
JelleZijlstra commentedon Aug 11, 2025
This is true for quite a few C-implemented classes, for example (in the
typesmodule):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.dchevell commentedon Aug 11, 2025
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__withNoReturn… 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
typeis assignable toCallable, so even if explicit usage of these types could theoretically be fixed it still wouldn't work in the context of generics wheretype[T]is considered assignable toCallable[..., 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 assumetypeis 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?srittau commentedon Aug 11, 2025
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).
JukkaL commentedon Aug 15, 2025
Would it be practical to make these abstract (i.e. ABCs)?
JelleZijlstra commentedon Aug 16, 2025
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.
dchevell commentedon Aug 16, 2025
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 ofNevermight 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