Problem:
I am working on a library with, e.g., support for a UInt5 type, an Int33 type, etc. The library is a little more complicated than this, but for the sake of example creating a UInt12 type might go
def makeUInt(size:int) -> type[UInt]:
class NewUInt(UInt):
# Do stuff with size
NewUInt.__name__ = f"UInt{size}"
UInt12 = makeUInt(12)
an_example_number = UInt12(508)
My IDE’s (VS Code) IntelliSense feature then recognizes the type of an_example_number as UInt, rather than UInt9.
The Rub:
I do not expect dynamically declared types to be picked up by type-hinting. However, I have clearly specified UInt12 as a type alias, and in fact if I subclass instead of type-alias by going
def makeUInt(size:int) -> type[UInt]:
class NewUInt(UInt):
# Do stuff with size
NewUInt.__name__ = f"UInt{size}"
class UInt12(makeUInt(12)): pass
an_example_number = UInt12(508)
everything works as intended, so clearly on some level the dynamic declaration can be coerced into something the IDE understands.
For example, I could, hypothetically, have UInt keep a register of created classes and prevent UInt12(makeUInt(12)) from actually subclassing. This is obviously not an ideal workaround, though.
The Ask:
How can I (preferably in Python 3.8) retain the advantage of dynamically creating types while getting the IDE to understand my preferred nomenclature for instances of those types?
The end-use case is that I want to provide certain types explicitly without redeclaring the # Do stuff with size information every time, so that common types like UInt16, UInt32, etc. can be declared in my library and receive hinting, whereas more uncommon types like UInt13 will be declared by users as needed and not necessarily receive hinting.
Back-of-the-box
def makeUInt(size:int) -> type[UInt]:
class NewUInt(UInt):
# Do stuff with size
NewUInt.__name__ = f"UInt{size}"
UInt12 = makeUInt(12)
an_example_number = UInt12(508)
I expected an_example_number to show up as a UInt12 by the type-hinter. It instead shows up as type[UInt].