Stop Fighting Circular Imports

The code is good, but you are missing the type hints!

Oh, I know, but as soon as I add them, I get these circular import errors!

Mmm, that’s often a smell, let’s dig a little

Sounds familiar? Yet sometimes even when digging in, the design is …


This content originally appeared on DEV Community and was authored by Nicolas Galler

The code is good, but you are missing the type hints!

Oh, I know, but as soon as I add them, I get these circular import errors!

Mmm, that's often a smell, let's dig a little

Sounds familiar? Yet sometimes even when digging in, the design is fine, and the modules really need to import each other, especially if you want to keep things simple and not introduce a Java-style interface hierarchy.

Turns out, modern Python has a solution for this, in fact, several of them. Let's check it out :)

Here a simple example:

# post.py
from models.user import User

class Post:
    def __init__(self, author: User):
        self.author = author

# user.py
from models.post import Post

class User:
    def __init__(self, name: str, posts: list[Post]):
        self.name = name
        self.posts = posts

This gives:

ImportError: cannot import name 'Post' from partially initialized module 'models.post' (most likely due to a circular import) (/home/nico/scratch/models/post.py)

So, first solution, from PEP-563, was to be able to do the annotations as strings.

class Post:
    def __init__(self, author: "User"):
        self.author = author

But that's not enough for most tools, you also need to tell it where to find User... yet you can't just import it as it would be back to a circular import, so you use a special "TYPE_CHECKING" guard:

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from models.user import User 

class Post:
    def __init__(self, author: "User"):
        self.author = author

And finally you can turn on these string annotations for the whole module using a future import:

from __future__ import annotations
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from models.user import User 

class Post:
    def __init__(self, author: User):
        self.author = author

Now, as often in Python, there are a few layers added over time. For the full details, you'll want to read the PEPs:

  • start with PEP-563, which is rather simple, and is available today (and as early as Python 3.7)
  • then move on PEP-649 and its little sister PEP-749. They change the way this is done, but this won't be available until Python 3.14.

Then, under the hood things will be quite different and it won't use the "string" annotations anymore. For most usage (when annotating code for static type hints), you will just be able to drop the __future__ import.


This content originally appeared on DEV Community and was authored by Nicolas Galler


Print Share Comment Cite Upload Translate Updates
APA

Nicolas Galler | Sciencx (2025-10-05T16:09:01+00:00) Stop Fighting Circular Imports. Retrieved from https://www.scien.cx/2025/10/05/stop-fighting-circular-imports/

MLA
" » Stop Fighting Circular Imports." Nicolas Galler | Sciencx - Sunday October 5, 2025, https://www.scien.cx/2025/10/05/stop-fighting-circular-imports/
HARVARD
Nicolas Galler | Sciencx Sunday October 5, 2025 » Stop Fighting Circular Imports., viewed ,<https://www.scien.cx/2025/10/05/stop-fighting-circular-imports/>
VANCOUVER
Nicolas Galler | Sciencx - » Stop Fighting Circular Imports. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/10/05/stop-fighting-circular-imports/
CHICAGO
" » Stop Fighting Circular Imports." Nicolas Galler | Sciencx - Accessed . https://www.scien.cx/2025/10/05/stop-fighting-circular-imports/
IEEE
" » Stop Fighting Circular Imports." Nicolas Galler | Sciencx [Online]. Available: https://www.scien.cx/2025/10/05/stop-fighting-circular-imports/. [Accessed: ]
rf:citation
» Stop Fighting Circular Imports | Nicolas Galler | Sciencx | https://www.scien.cx/2025/10/05/stop-fighting-circular-imports/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.