Fly, Penguin!

I blog so I don't forget.

Circular imports for Python typing annotations

1 minute read #snippet #solved

Problem:

# ###############################################################
# models.py
from controllers import BookController

class Book:
    def get_controller(self) -> BookController:
        return BookController(self)

# ###############################################################
# controllers.py
from models import Book

class BookController:
    def __init__(self, book: Book) -> None:
        self.book = book

Do you see it? Try it out - it will generate a “circular import” Exception.

The solution is very nicely described in Adam Johnson’s blog post (credit where credit’s due).

Copy-pasted for myself (still, please visit Adam’s page!!, he explains it much better and with more detail), this is it:

# ###############################################################
# models.py
from __future__ import annotations
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from controllers import BookController

class Book:
    def get_controller(self) -> BookController:
        return BookController(self)

# ###############################################################
# controllers.py
from __future__ import annotations
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from models import Book

class BookController:
    def __init__(self, book: Book) -> None:
        self.book = book

This fucking rocks. :))