2

Base class with abstract method _conn()

from cached_property import cached_property

class Base:
    
    def __init__(self, conn_id):
        """Store secrets details"""
        self.conn_id = conn_id
    
    @cached_property    
    def _conn(self):
        """Fetch secrets and generate authenticated client"""
        raise NotImplemented
    
    def get_conn(self):
        """Return authenticated client"""
        return self._conn

Parent classes

from cached_property import cached_property

class Parent(Base):

    @cached_property
    def _conn(self):
        """Generate authenticated client specific for Parent"""
        client = ...
        return client

Child classes

from typing import List, Optional
from pydantic.dataclasses import dataclass


@dataclass
class Toy:
    name: str
    type: str
    
    def get_color(self) -> str:
        color = self.get_conn().get_toy_color(...)
        return color
    

@dataclass
class Child:
    first_name: str
    last_name: str
    ...

    def list_all_toys(self) -> List[Toy]:
        all_toys = self.get_conn().fetch_all_toys(...)
        return [Toy(name=x.toy_name, type=x.toy_type) for x in all_toys]

    def get_favorite_toy(self) -> Optional[Toy]:
        favorite_toy = self.get_conn().fetch_favorite_toy(...)
        if not favorite_toy:
            return None
        return Toy(name=favorite_toy.toy_name, type=favorite_toy.toy_type)

(Ideal) Usage

parent = Parent(conn_id='my-secret-connection-details')

child_1 = parent.Child(first_name='John', last_name='Doe')

for each_toy in child_1.list_all_toys():
    print(f"{child_1.first_name}'s toy {each_toy.name} is a {each_toy.get_color()} {each_toy.type}.")
    # John's toy Teddy is a white stuffed bear.

Important notes

  • The parent class can be an ordinary Python class, but the children classes should be Python/Pydantic dataclasses.
  • The goal of using the same get_conn() method is to reduce the number attempts to fetch credentials and to authenticate.

I've thought about solving this using a @classmethod of the children classes that returns an authenticated instance. Seemed promising until I realized that dataclasses don't allow you to modify their __init__ method. For example:

from typing import Callable, Optional
from pydantic.dataclasses import dataclass

@dataclass
class Toy:
    ...
    @classmethod
    def generate_with_connection(cls, connection: Callable, *args, **kwargs):
        return cls(*args, **kwargs, connection=connection) # Requires logic in __init__ to handle `connection`.

@dataclass
class Child:
    ...
    def get_favorite_toy(self) -> Optional[Toy]:
        favorite_toy = self.get_conn().fetch_favorite_toy(...)
        if not favorite_toy:
            return None
        return Toy.generate_with_connection(
            connection=self.get_conn,
            name=favorite_toy.toy_name,
            type=favorite_toy.toy_type
        )

Questions

  1. How do you link the parent class and multiple children classes to ensure that each child class can access the same get_conn() method of the parent class? My first guess would be inheritance but I don't think it solves the next question. Are there other ways using the inspect/traceback modules?
  2. How do we ensure that the methods of each children class can return instances of other children classes that also have access to the same get_conn() method of the parent class? For example: Child.get_favorite_toy() should return an instance of Toy which can successfully run Toy.get_color() using the same get_conn() method.
hamzaahmad
  • 133
  • 2
  • 7
  • 1
    dataclasses have a `__post_init__` method, you can declare a field as [init-only](https://docs.python.org/3/library/dataclasses.html#init-only-variables), and the `__init__` generation is *optional* (use `@dataclass(init=False)` to disable the method being generated). – Martijn Pieters Aug 18 '21 at 17:20
  • 1
    Your 'child classes' are otherwise not child classes if you are not using inheritance. Why, if I may ask, are you not just using inheritance? – Martijn Pieters Aug 18 '21 at 17:22
  • I agree that the child classes aren't actual child classes. I initially structured the classes using inheritance until I realized that I would have trouble solving Question 2 where I need to be able to return instances of the children that are tied to the same instance of the parent instead of a new instance. Init-only variables looks interesting! I'll read through the docs and try to implement to see if it solves both issues – hamzaahmad Aug 18 '21 at 17:27
  • If the connections must be reused, then use some kind of connection pool to retrieve connections from. Using a cached property on a class is not a suitable solution here. – Martijn Pieters Aug 18 '21 at 17:30

0 Answers0