Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Define a custom Type that behaves like typing.Any

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:

  1. Doesn't work, because you can't subclass Any: TypeError: Cannot subclass <class 'typing._SpecialForm'>

    class MyAny(Any):
       pass
    
  2. 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
    
  3. 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
    
  4. 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?

like image 268
RunOrVeith Avatar asked Sep 06 '25 03:09

RunOrVeith


1 Answers

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"
like image 138
Markus Unterwaditzer Avatar answered Sep 07 '25 23:09

Markus Unterwaditzer