I need to create a Type that behaves like typing.Any
when looked at by the type checker (mypy), but is distinguishable from typing.Any
.
The use case is some pretty "meta" code that needs to find the variable that is annotated with this type out of a set of other variables that could be annotated with typing.Any
.
Note that I will never have to actually make an instance of this Type, I just need it for type annotations in the context of dataclasses.
Example:
from dataclasses import dataclass, fields
from typing import Any
MyAny = ... # What to put here?
@dataclass()
class Test:
a: Any
b: MyAny = None
for field in fields(Test):
if field.type == MyAny:
print(f"It's {field.name}") # This should print "It's b"
Things I have tried:
Doesn't work, because you can't subclass Any: TypeError: Cannot subclass <class 'typing._SpecialForm'>
class MyAny(Any):
pass
Doesn't work, because it is not distinguishable from the normal Any (result of code snipped above is It's a\nIt's b
)
MyAny = Any
Works at runtime, but mypy complains about the default value:
Mypy: Incompatible types in assignment (expression has type "None", variable has type "MyAny")
class MyAny:
pass
Works at runtime, but mypy can't tell that this should behave like Any
:
It complains about the definition that
Mypy: Argument 2 to NewType(...) must be subclassable(got "Any")
and it complaints about the default parameter:
Mypy: Incompatible types in assignment (expression has type "None", variable has type "MyAny")
from typing import NewType
MyAny = NewType("MyAny", Any)
So is there a way to make this work?
You can use conditionals to trick mypy into interpreting one piece of code while having your runtime execute another one.
from dataclasses import dataclass, fields
from typing import Any
if False:
MyAny = Any
else:
class MyAny: # type: ignore
pass
@dataclass()
class Test:
a: Any
b: MyAny = None
for field in fields(Test):
if field.type == MyAny:
print(f"It's {field.name}") # This should print "It's b"