Master how Python organizes, imports, and resolves code across production backend services. Demystify sys.path, master absolute vs relative imports, structure clean layered packages with __init__.py, eliminate circular dependency traps, and manage dependencies with virtual environments and modern pyproject.toml.
Every .py file is a module with its own isolated namespace.
In Python, a module is simply a file containing Python code ending in .py. Modules provide a distinct namespace, meaning functions, classes, and variables defined inside db.py do not pollute or collide with identifiers inside auth.py.
# 1. Whole module import (explicit namespace) import app.config.settings # 2. Specific object import (concise) from app.config.settings import DATABASE_URL # 3. Import alias (disambiguation) from app.database import client as db_client
Rule of thumb: Avoid wildcard imports like from app.models import *! They obscure where identifiers originate, break IDE autocomplete, and risk accidental symbol shadowing.
When you write import x, Python follows a strict search order:
sys.modules): If already loaded in memory, it returns the cached module instance immediately.sys, math).sys.path): The directory of the executed script, followed by PYTHONPATH, and finally virtual environment site-packages.# File: app/database/migrations.py
def run_migrations():
print("Applying database schema migrations...")
# When this file is imported: __name__ is "app.database.migrations" (code below DOES NOT run)
# When this file is executed directly: python app/database/migrations.py -> __name__ is "__main__"
if __name__ == "__main__":
print("Running migration script directly from CLI:")
run_migrations()config.py — reads environment variables and yields typed settingsdatabase.py — manages connection pools and session lifecyclesservices.py — contains pure business logic with zero HTTP couplinghelpers.py — deterministic pure utility functions (formatting, date math)Structuring directories into hierarchical namespaces using __init__.py.
A package is a directory on the file system containing Python modules and typically an __init__.py file. Packages allow dot-notated hierarchical namespaces such as app.services.auth.
Role of __init__.py:
__all__ = ["AuthService"].__init__.py, but in backend apps, explicit __init__.py files are standard practice to declare package boundaries.Absolute Imports (Recommended):from app.services.user_service import get_user
Explicit, unambiguous, and works regardless of current working directory.
Relative Imports (Intra-Package Only):from . import models (sibling module)from ..database import get_db (parent package)
Gotcha: Relative imports ONLY work when the module is imported as part of a package. If you run python app/services/user_service.py directly, relative imports immediately crash with ValueError: attempted relative import beyond top-level package!
Managing standard library, local code, and third-party PyPI packages.
Shipped with Python. Zero external installation required:
• os, sys, pathlib
• json, asyncio, datetime
• typing, logging, hashlib
Your proprietary business logic, routes, and schemas:
• app.services.order_service
• app.routes.auth
• app.config.settings
Installed into your virtual environment via pip:
• fastapi, uvicorn
• pydantic, httpx
• sqlalchemy, alembic
fastapi==0.115.0 uvicorn[standard]==0.30.6 pydantic==2.9.2 httpx==0.27.2
Flat list of pinned versions. Still widely used for Docker builds, but lacks metadata, build backend declarations, and unified tool configuration.
[project]
name = "backend-api"
version = "1.0.0"
dependencies = [
"fastapi>=0.115.0",
"uvicorn>=0.30.0",
"pydantic>=2.9.0",
]
[tool.pytest.ini_options]
testpaths = ["tests"]The unified standard for modern Python tooling (Hatch, Flit, Ruff, UV, Pytest).
How to read tracebacks and systematically solve import failures in backend projects.
| Error Type | Root Cause | How to Diagnose & Fix |
|---|---|---|
ModuleNotFoundError:No module named 'app' | Python cannot find the package because the project root directory is missing from sys.path. | Fix: Run your script from project root using the -m module flag: python -m app.main instead of python app/main.py. |
ImportError:cannot import name 'X' from partially initialized module | Circular Import: Module A imports Module B, which simultaneously imports Module A before initialization completes. | Fix: Decouple the shared symbols into a separate leaf module (e.g. models.py or types.py) that imports neither A nor B. |
ValueError:attempted relative import beyond top-level package | Running a script with relative imports (from ..service import x) directly via python script.py instead of importing it within a package. | Fix: Use absolute imports, or execute as a module via python -m app.services.script. |
AttributeError:module 'app.routes' has no attribute 'users' | Confusing a package directory with a module, or forgetting to expose the sub-module in __init__.py. | Fix: Explicitly import the submodule: from app.routes import users. |
Fix the broken imports across this e-commerce backend service and execute python -m app.main.
Test your package design intuition and review the core rules of Python backend architecture.
You need to add JWT authentication token signing & password hashing to your backend. Where should this logic be placed to maintain clean architecture and prevent circular imports?
.py file has its own namespace. Use explicit imports and avoid import *.__init__.py to declare package boundaries and public exports.from app.services import ... over deep relative imports. It is clearer and survives file restructuring.python -m app.main to ensure sys.path includes the root directory.python -m venv .venv to prevent version collisions.pyproject.toml for standard dependencies and unified tool configuration across teams.