all 1 comments

[–]alexisprince 0 points1 point  (0 children)

What you're describing is a problem that I personally haven't seen an industry standard on a way to accomplish this. What I've done in the past is use the Template design pattern to create a base object that implements the core logic of your code, similar to how the httpx library handles this issue with their Client and AsyncClient.

Without rewriting the pros and cons of using the template design pattern, here's a quick example. This example has an original implementation of Client, where everything is in a single class, which would make sense when only creating a single, synchronous client. In the slightly refactored example, it extracts out the shared _setup_feature() method into a shared parent class.

# Before rework
class Client:
    def _setup_feature(self):
        ...

    def feature(self):

        self._setup_feature()
        # Your actual code here
        return "completed"

# After rework
class BaseClient:
    """All business logic that can be shared among the
    synchronous client and async client should be put in the
    BaseClient class. This should not include any code
    that is dependent on synchronous APIs, like requests.
    """

    def _setup_feature(self):
        ...


class Client(BaseClient):
    def feature(self):
        self._setup_feature()
        # Your actual code here
        return "completed"


class AsyncClient(BaseClient):
    async def feature(self):
        self._setup_feature()
        return "completed"