jeudi 20 juillet 2023

How to avoid circular imports when using Python type hints

I'm trying to figure out what would be the best solution to avoid circular imports in a situation where I'm using Protocol and type hints. The problem is the following:

I have some storage targets that in the push method receives and Event.

from typing import Protocol

from event import Event


class StorageProtocol(Protocol):
    def push(self, event: Event):
        ...

class StoragePostgres(StorageProtocol):
    def push(self, event: Event):
        """pushes Event to Postgres"""
        pass

class StorageS3(StorageProtocol):
    def push(self, event: Event):
        """pushes Event to S3"""
        pass

And I have some event that has a function to store itself in something that implements the Protocol StorageProtocol.

from dataclasses import dataclass
from storage import StorageProtocol

@dataclass
class Event:
    id: str
    start_datetime: str
    end_datetime: str
    
    def push(self, storage: StorageProtocol):
        storage.push(self)

This results in a circular import error because the storage.py imports Event and event.py imports StorageProtocol.

I'm aware of using some approaches like the following

  1. from __future__ import annotations
  2. from typing import TYPE_CHECKING
  3. Importing only within the class
  4. Using same if statements to avoid import at run time
  5. Remove type hint
  6. Using type hints in quotes

But none of them seem like a good way to keep the code clean. What am I missing to not figure out how to make this simple example clean?

Aucun commentaire:

Enregistrer un commentaire