ImportError: cannot import name X from partially initialized module (circular import)"This error means two modules end up needing each other before either one has finished loading. Python doesn't get confused by this in the way a stack overflow suggests; it does something more specific, and once you see what, the fix is usually a one-line move rather than a redesign. This article reproduces the two most common real-world shapes of the problem, two files importing each other directly and a submodule importing back from its own package, and covers the fix for each.
Take a small package with a models module and a utils module, each importing something from the other at the top of the file:
# pkg/models.py
from pkg.utils import validate
class User:
def __init__(self, name):
validate(name)
self.name = name
# pkg/utils.py
from pkg.models import User
def validate(name):
if not name:
raise ValueError("empty name")
def make_default_user():
return User("default")
# main.py
from pkg.models import User
u = User("sebastian")
Running main.py fails before User is ever constructed:
Traceback (most recent call last):
File "main.py", line 1, in
from pkg.models import User
File "pkg/models.py", line 1, in
from pkg.utils import validate
File "pkg/utils.py", line 1, in
from pkg.models import User
ImportError: cannot import name 'User' from partially initialized module 'pkg.models'
(most likely due to a circular import) (pkg/models.py)
Trace the exact sequence: main.py imports pkg.models. Python creates a (still-empty) module object for pkg.models, registers it in sys.modules immediately, then starts running its code line by line. Line 1 of models.py imports pkg.utils, so Python pauses models.py and starts running utils.py. Line 1 of utils.py imports pkg.models again, but pkg.models is already sitting in sys.modules, mid-execution, stopped after only its own first line. Python doesn't restart it. It hands utils.py that same in-progress module object, and User isn't defined in it yet, because execution never got past line 1. That missing attribute is the actual error.
Moving the import that closes the cycle from module level into the function that actually needs it breaks the deadlock, because a function-local import only runs when the function is called, not while the module is still being built:
# pkg/utils_fixed.py
def validate(name):
if not name:
raise ValueError("empty name")
def make_default_user():
# deferred: this import only runs when make_default_user() is called,
# by which point pkg.models_fixed has already finished loading
from pkg.models_fixed import User
return User("default")
# pkg/models_fixed.py
from pkg.utils_fixed import validate
class User:
def __init__(self, name):
validate(name)
self.name = name
from pkg.models_fixed import User
u = User("sebastian")
print(u.name)
# sebastian
from pkg.utils_fixed import make_default_user
d = make_default_user()
print(d.name)
# default
Both directions work now. models_fixed.py still imports validate from utils_fixed at module level, which is fine, that direction was never the problem. Only the import that pointed back and closed the loop needed to move.
The more common real-world trigger looks nothing like two files openly importing each other; it hides inside an ordinary-looking package import. A package's __init__.py commonly re-exports its submodules' contents so callers can write from mypackage import Thing instead of a longer path. If one of those submodules tries to import something back from the package itself, the cycle is the same one, just one layer less obvious:
# mypackage/__init__.py
from mypackage.trainer import Trainer
from mypackage.config import DEFAULT_CONFIG
# mypackage/config.py
DEFAULT_CONFIG = {"lr": 0.001}
# mypackage/trainer.py
from mypackage import DEFAULT_CONFIG # importing back from the package's own __init__
class Trainer:
def __init__(self, config=None):
self.config = config or DEFAULT_CONFIG
# main.py
from mypackage import Trainer
Traceback (most recent call last):
File "main.py", line 1, in
from mypackage import Trainer
File "mypackage/__init__.py", line 1, in
from mypackage.trainer import Trainer
File "mypackage/trainer.py", line 1, in
from mypackage import DEFAULT_CONFIG
ImportError: cannot import name 'DEFAULT_CONFIG' from partially initialized module 'mypackage'
(most likely due to a circular import) (mypackage/__init__.py)
__init__.py hadn't reached its second line, the one defining DEFAULT_CONFIG in the package's namespace, because it was still busy running trainer.py from its first line. trainer.py asked the package for something the package hadn't gotten around to providing yet.
The line that actually causes the problem is trainer.py asking mypackage's own namespace for a value that lives in a sibling submodule. Importing that submodule directly instead skips __init__.py's in-progress namespace entirely:
# mypackage/trainer_fixed.py
from mypackage.config import DEFAULT_CONFIG # the submodule directly, not the package
class Trainer:
def __init__(self, config=None):
self.config = config or DEFAULT_CONFIG
# mypackage/__init__.py
from mypackage.trainer_fixed import Trainer
from mypackage.config import DEFAULT_CONFIG
from mypackage import Trainer
t = Trainer()
print(t.config)
# {'lr': 0.001}
Nothing about trainer_fixed.py's logic changed, only which path it uses to reach DEFAULT_CONFIG. mypackage.config has no dependency on anything else in the package, so importing it directly never has to wait on __init__.py to finish.
A specific version of this problem shows up in type-annotated code: module A needs to import class B purely to write a type hint like def process(item: B) -> None, but B's module already imports something from A, creating a cycle that only exists for the benefit of a static type checker, never for anything the code actually runs. The standard library's typing.TYPE_CHECKING flag is built for exactly this: it's True while a type checker is analyzing the file, and False at real runtime, so an import guarded by it never executes and never participates in the cycle:
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from pkg.models import User # only seen by type checkers, never imported at runtime
def process(item: "User") -> None:
...
This only helps when the import genuinely exists for a type hint and nothing else. If the function body actually calls something from the imported module at runtime, this pattern will raise a NameError the moment that line executes, and the lazy-import fix above is the one that applies instead.
Situation Fix
────────────────────────────────────────────────── ─────────────────────────────────────────
Two modules import each other directly Move the closing import inside a function
A submodule imports back from its own __init__.py Import the specific submodule directly,
not the package
The same cycle keeps recurring as code grows Move the shared code into a third,
lower-level module both can depend on
Import only exists to satisfy a type hint Guard it with if TYPE_CHECKING: and quote
the annotation as a string