4

Following a solution described in this question: How to access the type arguments of typing.Generic?

I have successfully solved a generic type at runtime in Python 3.8. However I can't seem to be able to access the __args__ field described in the solution for Python 3.6. I'm not sure why, here's my code:

def get_generic_type_arg(cls):
    if py_version >= (3, 8):
        t = cls.__orig_bases__[0]
        return get_args(t)[0]
    else:
        t = cls.__args__
        return None

class EventContract:
    @classmethod
    def get_version(cls) -> int:
        return int(cls.__name__[1:])


RT = TypeVar('RT', bound=EventContract)


class QueryContract(Generic[RT], EventContract):
    """ Base class for query contracts that are versioned. """

    @classmethod  # Do not override this
    def get_response_class(cls) -> Type[RT]:
        return get_generic_type_arg(cls)

    def build_result(self, *args, **kwargs):
        return self.get_response_class()(**kwargs)


@dataclasses.dataclass
class V1Response(EventContract):
    value: int


@dataclasses.dataclass
class V1(QueryContract[V1Response]):
    id: int
    name: str

When trying to call get_generic_type on cls I get an AttributeError. I also tried to fetch its base class using t.__bases__[0] which gives me the QueryContract with no generic arguments (__args__ gives me another attribute error). I am calling the get_response_class method on V1. Any ideas ?

BinarSkugga
  • 400
  • 2
  • 15
  • I have realised that Python 3.6 doesn't support dataclasses anyways. Still interested in the solution with normal classes though. – BinarSkugga Dec 13 '21 at 18:02

1 Answers1

1

I have figured out how to do it. For some reason, __orig_bases__ wasn't working but after rebuilding my Python 3.6 and using a VENV, it works. This is the final working method:

py_version = sys.version_info
if py_version >= (3, 8):
    from typing import get_args


def get_generic_type_arg(cls):
    t = cls.__orig_bases__[0]
    if py_version >= (3, 8):
        return get_args(t)[0]
    else:
        return t.__args__[0]

Python 3.6 also requires to install dataclasses as a library though it wasn't part of this issue. Also note that __args__ seems to be undocumented in Python 3.6 and removed from Python 3.8.

BinarSkugga
  • 400
  • 2
  • 15
  • 1
    Feels too low level, I'd rather pass my type as a regular variable if I needed this level of introspection and it [wasn't supported through a stable language facade](https://stackoverflow.com/q/57706180/1509695) – matanster Jul 02 '23 at 14:02