# How to Fix Python Import Circular Dependency
Circular import errors occur when two or more modules depend on each other, creating an import loop that Python cannot resolve.
Error Patterns
Import Error
```text ImportError: cannot import name 'SomeClass' from partially initialized module 'module_a' (most likely due to a circular import)
ImportError: cannot import name 'User' from 'models' (most likely due to a circular import) ```
AttributeError During Import
AttributeError: partially initialized module 'module_a' has no attribute 'SomeClass'
(most likely due to a circular import)Example Circular Import
```python # module_a.py from module_b import ClassB
class ClassA: def __init__(self): self.b = ClassB()
# module_b.py from module_a import ClassA
class ClassB: def __init__(self): self.a = ClassA() # Circular! ```
Diagnosis Steps
Step 1: Identify the Cycle
```bash # Use import time profiling python -X importtime your_script.py 2>&1 | grep "import"
# Or use import-linter pip install import-linter lint-imports ```
Step 2: Visualize Dependencies
# Add debug prints to see import order
print("Importing module_a")
from module_b import ClassB
print("module_b imported")Step 3: Check Module Loading State
```python import sys
# Check if module is being loaded if 'module_a' in sys.modules: print("module_a already loaded") else: print("module_a not yet loaded") ```
Solutions
Solution 1: Move Import Inside Function (Deferred Import)
```python # module_a.py # DON'T: Import at module level # from module_b import ClassB
class ClassA: def __init__(self): # DO: Import inside method from module_b import ClassB self.b = ClassB() ```
Solution 2: Restructure Modules
Move shared code to a third module:
```python # types.py (new module for shared types) class SharedType: pass
# module_a.py from types import SharedType
class ClassA: pass
# module_b.py from types import SharedType from module_a import ClassA
class ClassB: pass ```
Solution 3: Use TYPE_CHECKING for Type Hints
```python # module_a.py from typing import TYPE_CHECKING
if TYPE_CHECKING: from module_b import ClassB
class ClassA: def __init__(self, b: 'ClassB') -> None: self.b = b
def get_b(self) -> 'ClassB': return self.b ```
Solution 4: Dependency Injection
```python # module_a.py class ClassA: def __init__(self, b_factory=None): if b_factory is None: from module_b import ClassB b_factory = ClassB self.b = b_factory()
# module_b.py from module_a import ClassA
class ClassB: pass
# main.py from module_a import ClassA from module_b import ClassB
a = ClassA(b_factory=ClassB) ```
Solution 5: Import Only When Needed
```python # config.py - No circular dependency class Config: pass
# database.py from config import Config
class Database: def __init__(self, config: Config): self.config = config
# app.py - Import order matters from config import Config from database import Database
config = Config() db = Database(config) ```
Solution 6: Use __all__ and Late Imports
```python # module_a.py __all__ = ['ClassA']
class ClassA: def method(self): from module_b import ClassB return ClassB()
# module_b.py __all__ = ['ClassB']
class ClassB: def method(self): from module_a import ClassA return ClassA() ```
Solution 7: Interface Pattern
```python # interfaces.py from abc import ABC, abstractmethod
class IClassA(ABC): @abstractmethod def method_a(self): ...
class IClassB(ABC): @abstractmethod def method_b(self): ...
# module_a.py from interfaces import IClassA, IClassB
class ClassA(IClassA): def __init__(self, b: IClassB): self.b = b
def method_a(self): return self.b.method_b()
# module_b.py from interfaces import IClassB
class ClassB(IClassB): def method_b(self): return "result" ```
Solution 8: Factory Pattern
```python # factories.py class Factory: _class_a = None _class_b = None
@classmethod def get_class_a(cls): if cls._class_a is None: from module_a import ClassA cls._class_a = ClassA return cls._class_a
@classmethod def get_class_b(cls): if cls._class_b is None: from module_b import ClassB cls._class_b = ClassB return cls._class_b
# module_a.py class ClassA: def __init__(self): from factories import Factory self.b = Factory.get_class_b()() ```
Common Patterns and Fixes
Django Model Circular Import
```python # DON'T: Import model at module level # from app.models import User
def get_user(user_id): # DO: Import inside function from app.models import User return User.objects.get(id=user_id) ```
Flask Application Factory
```python # extensions.py from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy()
# models.py from extensions import db
class User(db.Model): pass
# app.py from flask import Flask from extensions import db from models import User
def create_app(): app = Flask(__name__) db.init_app(app) return app ```
Pydantic Model Circular Reference
```python from __future__ import annotations from typing import Optional from pydantic import BaseModel
class User(BaseModel): name: str friend: Optional[User] = None # Self-reference
# Or use model_rebuild class User(BaseModel): name: str friend: Optional['User'] = None
User.model_rebuild() # Resolve forward references ```
Prevention Tips
- 1.Organize code by feature, not by type
- 2.Use dependency injection for cross-module dependencies
- 3.Keep imports at the top when possible, use deferred imports sparingly
- 4.Use TYPE_CHECKING for type hints that cause cycles
- 5.Create interface modules for shared types
Project Structure to Avoid Cycles
project/
├── core/
│ ├── __init__.py
│ └── interfaces.py # Shared interfaces/types
├── module_a/
│ ├── __init__.py
│ └── service.py # Depends on core only
├── module_b/
│ ├── __init__.py
│ └── service.py # Depends on core only
└── main.py # Wires everything togetherTools for Detection
```bash # Install import-linter pip install import-linter
# Create .importlinter config cat > .importlinter << EOF [importlinter] root_packages = mypackage
[importlinter:contract:modules-should-not-import-from-outside] type = layers layers = mypackage.core | mypackage.services | mypackage.api EOF
# Run check lint-imports ```
Related Errors
ModuleNotFoundError- Module cannot be foundImportError- Module found but import failedAttributeError- Module found but attribute missing