I am interested in creating a generic to add a fixed functionality to different types in python. I have a semi working solution, but it's tying is problematic.
Here's a simplified version of what I'm trying:
from __future__ import annotations
from typing import Generic, Set, TypeVar, Type, Callable
def CreateTaggedType(base_type: type):
T = TypeVar("T")
class TaggedTypeGenerator(Generic[T]):
def __init__(self):
pass
def tag_provider(self, parent: Type[T]):
class Imp(parent):
def __init__(self, **args):
super().__init__(**args)
self.tags: Set[str] = set()
def add_tag(self, tag):
self.tags.add(tag)
def remove_tag(self, tag):
if tag in self.tags:
self.tags.remove(tag)
return True
return False
return Imp
return TaggedTypeGenerator[base_type]().tag_provider(parent=base_type)
class Counter:
def __init__(self, count=0):
self.count = count
def inc(self):
self.count += 1
return self.count
# Create an alias for a new type of tagged Counter
TaggedCounter = CreateTaggedType(Counter)
tc = TaggedCounter(count=2)
print(tc.inc()) # should print "3"
tc.add_tag("foo")
print(tc.remove_tag("bar")) # should print "False"
print(tc.remove_tag("foo")) # should print "True"
print(tc.inc()) # should print "4"
The output:
3
False
True
4
Technically, this works, but the type isn't correctly inferred. This causes auto-completion to not work properly. I only get aut-ocompletion for the added functionality, and not for anything related to the base class.
How can I make it so that the type is correct?
I tried this variation using abc.ABCMeta with no effect:
from __future__ import annotations
import abc
from typing import Callable, Generic, Set, Type, TypeVar
T = TypeVar("T")
class TaggedTypeGenerator(Generic[T]):
def __init__(self, parent: Type[T]):
self.parent = parent
class _imp_(self.parent):
def __init__(self, **args):
super().__init__(**args)
self.tags: Set[str] = set()
def add_tag(self, tag):
self.tags.add(tag)
def remove_tag(self, tag):
if tag in self.tags:
self.tags.remove(tag)
return True
return False
self.Imp = _imp_
class Counter:
def __init__(self, count=0):
self.count = count
def inc(self):
self.count += 1
return self.count
def CreateTaggedTypeGenerator(base_type: T) -> TaggedTypeGenerator[T]:
return TaggedTypeGenerator[base_type](base_type)
# Create an alias for a new type of tagged Counter
TaggedCounterGererator = CreateTaggedTypeGenerator(Counter)
class counter_meta(metaclass=abc.ABCMeta):
pass
counter_meta.register(Counter)
@counter_meta.register
class TaggedCounter(TaggedCounterGererator.Imp):
pass
tc = TaggedCounter(count=2)
print(tc.inc()) # should print "3"
tc.add_tag("foo")
print(tc.remove_tag("bar")) # should print "False"
print(tc.remove_tag("foo")) # should print "True"
print(tc.inc()) # should print "4"
This also procudes the expected output. However, not only does this not solve the typing issue, it makes it worse. With this, auto-complete doesn't work for the base class and the added functions.
Any suggestions would are welcome.
NOTE: My editor is VS Code, I don't think it matters much here, but...