Skip to content

Shared Packages Management Strategy

Status: Analysis & Recommendations Last Updated: 2025-10-25 Purpose: Eliminate "module not found" errors across local, Docker, and Azure ACR deployments Related Docs: Azure Deployment Architecture

Executive Summary

This document analyzes the recurring "module not found" issues for shared packages (loan_defenders_models and loan_defenders_utils) across different deployment environments and provides best practices to ensure consistent, reliable imports.

Current Status: Packages use uv workspace with editable installs locally, multi-stage Docker builds with PYTHONPATH, and potential ACR build context issues.

Root Cause: Inconsistent package installation strategies across environments (editable installs vs. regular installs, PYTHONPATH dependency, build context variations).

Recommended Solution: Standardize on proper Python package installation with consistent patterns across all environments, eliminate PYTHONPATH dependency.


Table of Contents

  1. Current Architecture Analysis
  2. Root Causes of Module Not Found
  3. Environment-Specific Issues
  4. Best Practices Comparison
  5. Recommended Strategy
  6. Implementation Plan
  7. Verification Checklist

Current Architecture Analysis

Package Structure

loan-defenders/
├── pyproject.toml                      # Workspace root configuration
├── uv.lock                             # Lock file for all dependencies
├── loan_defenders_models/              # Shared domain models
│   ├── pyproject.toml                 # Package configuration
│   ├── __init__.py
│   ├── application.py
│   ├── assessment.py
│   ├── decision.py
│   └── responses.py
├── loan_defenders_utils/               # Shared utilities
│   ├── pyproject.toml                 # Package configuration
│   ├── __init__.py
│   ├── azure_credential.py
│   ├── mcp_config.py
│   ├── mcp_transport.py
│   └── observability.py
└── apps/
    ├── api/                           # FastAPI application
    │   ├── pyproject.toml
    │   └── loan_defenders/
    ├── ui/                            # React frontend
    └── mcp_servers/                   # MCP server implementations
        ├── application_verification/
        ├── document_processing/
        └── financial_calculations/

Current Configuration (Root pyproject.toml)

Workspace Setup:

[tool.uv.workspace]
members = [
    "apps/api",
    "loan_defenders_models",
    "loan_defenders_utils",
    "apps/mcp_servers/application_verification",
    "apps/mcp_servers/document_processing",
    "apps/mcp_servers/financial_calculations",
]

[tool.uv.sources]
loan-defenders-utils = { workspace = true }
loan-defenders-models = { workspace = true }

Dependencies:

dependencies = [
    "loan-defenders-models",
    "loan-defenders-utils",
    "mcp[cli]>=1.12.3",
]

Package Configuration (loan_defenders_models/pyproject.toml)

[project]
name = "loan-defenders-models"
version = "0.1.0"
description = "Shared business domain models for Loan Defenders multi-agent system"
requires-python = ">=3.10"
dependencies = [
    "pydantic>=2.5.0",
]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.wheel]
packages = ["."]

[tool.hatch.build.targets.editable]
packages = ["."]

Issue Identified: Using flat layout (packages = ["."]) which can cause issues with package discovery.

Current Dockerfile Pattern (apps/api/Dockerfile)

# Stage 1: Builder
FROM python:3.11-slim AS builder
WORKDIR /app

# Copy workspace files
COPY pyproject.toml uv.lock ./

# Copy shared packages first
COPY loan_defenders_models ./loan_defenders_models
COPY loan_defenders_utils ./loan_defenders_utils

# Copy API package config
COPY apps/api/pyproject.toml ./apps/api/

# Install dependencies
RUN uv sync --frozen --no-dev --package loan-defenders-api

# Stage 2: Runtime
FROM python:3.11-slim
WORKDIR /app

# Copy venv and shared packages
COPY --from=builder /app/.venv /app/.venv
COPY --from=builder /app/loan_defenders_models /app/loan_defenders_models
COPY --from=builder /app/loan_defenders_utils /app/loan_defenders_utils

# Copy application code
COPY apps/api/loan_defenders ./loan_defenders

# Set PATH and PYTHONPATH
ENV PATH="/app/.venv/bin:$PATH" \
    PYTHONPATH="/app"

# Run application
CMD ["uvicorn", "loan_defenders.api.app:app", "--host", "0.0.0.0", "--port", "8000"]

Key Observation: Relies on PYTHONPATH=/app to make shared packages importable.


Root Causes of Module Not Found

1. PYTHONPATH Dependency (High Risk)

Problem: Current Docker setup depends on PYTHONPATH=/app environment variable.

Why This Is Fragile: - PYTHONPATH is not always respected (depends on how Python is invoked) - Can be overridden by other tools or configurations - Not standard Python packaging practice - Doesn't work in all execution contexts (e.g., some CI/CD runners)

Symptom:

>>> import loan_defenders_models
ModuleNotFoundError: No module named 'loan_defenders_models'

Root Cause: Packages not installed into Python's site-packages, relying on PYTHONPATH hack.

2. Inconsistent Installation Methods

Problem: Different installation methods across environments.

Environment Installation Method Result
Local Development uv sync (editable install) Works with workspace
Docker Builder uv sync --frozen --no-dev May or may not install shared packages
Docker Runtime COPY + PYTHONPATH Not properly installed
Azure ACR Build az acr build with context Build context may be incomplete

Root Cause: Shared packages not installed as proper dependencies in runtime environment.

3. Flat Layout Package Structure

Problem: Using packages = ["."] in hatch configuration.

Issue: This tells the build system to treat the package root as the package itself, which can confuse package discovery.

Better Approach: Use src layout or explicit package naming.

4. Azure ACR Build Context

Problem: When running az acr build, the build context might not include shared packages.

Example:

# This might fail if run from apps/api/
az acr build --registry myacr --image api:latest --file Dockerfile .

# Because Dockerfile expects:
COPY loan_defenders_models ./loan_defenders_models
# But loan_defenders_models is not in apps/api/, it's in repo root

Root Cause: Build context is limited to specified directory, doesn't include repo root.

5. Editable Install Confusion

Problem: uv sync in workspace creates editable installs locally, but Docker doesn't use editable installs.

Local (works):

$ uv sync
# Creates editable links in .venv/lib/python3.11/site-packages/
# loan_defenders_models.pth → points to /workspaces/loan-defenders/loan_defenders_models

Docker (doesn't work same way):

RUN uv sync --frozen --no-dev --package loan-defenders-api
# Installs loan-defenders-api and its dependencies
# But shared packages might not be in site-packages properly

Root Cause: Workspace editable installs don't translate directly to Docker isolated builds.


Environment-Specific Issues

Local Development

Current Approach: uv sync with workspace configuration.

Status: ✅ Generally works

Why It Works: - Editable installs via workspace - Packages linked from repo root - PYTHONPATH handled by uv/IDE

Potential Issues: - New developers might forget to run uv sync - IDE might not recognize workspace packages immediately - PYTHONPATH environment variable conflicts

Symptom:

$ python -c "import loan_defenders_models"
ModuleNotFoundError: No module named 'loan_defenders_models'
# Even though uv sync was run

Fix Required: Ensure proper package installation, not just editable links.

Docker Builds (Local)

Current Approach: Multi-stage build with COPY + PYTHONPATH.

Status: ⚠️ Works but fragile

Why It's Fragile: - Depends on PYTHONPATH environment variable - Shared packages not in site-packages - No proper installation of shared packages in venv

Symptom:

# In container:
$ python -c "import loan_defenders_models"
# Works if PYTHONPATH=/app is set
# Fails if PYTHONPATH is unset or overridden

Issue:

# If running Python differently:
$ cd /app
$ python -m uvicorn loan_defenders.api.app:app
# Might fail because uvicorn resets PYTHONPATH in some contexts

Fix Required: Install shared packages into venv during build stage.

Docker Builds (Azure ACR)

Current Approach: az acr build from repository root or subdirectory.

Status: ❌ High risk of failure

Potential Issues:

Issue 1: Build Context Location

# If deploying from subdirectory:
$ cd apps/api
$ az acr build --registry myacr --file Dockerfile .

# Dockerfile expects:
COPY loan_defenders_models ./loan_defenders_models  # ← Not in current directory!

Issue 2: Missing Lock File

# If uv.lock is not copied:
RUN uv sync --frozen  # ← Fails because no lock file

Issue 3: Workspace Resolution

# ACR build happens in isolation
# Workspace references like { workspace = true } don't work
# Because ACR doesn't understand monorepo structure

Fix Required: Ensure ACR builds run from repository root with full context.

Azure Container Instances (ACI) / Container Apps

Current Approach: Pull pre-built images from ACR, run with environment variables.

Status: ⚠️ Depends on image quality

Potential Issues: - If image wasn't built correctly (wrong context), runtime fails - PYTHONPATH might be overridden by orchestrator - No way to fix after deployment (need to rebuild image)

Symptom:

Container failed to start: ModuleNotFoundError: No module named 'loan_defenders_models'

Fix Required: Ensure images have properly installed packages.


Best Practices Comparison

Approach: Copy shared package source, set PYTHONPATH.

Pros: - ✅ Simple to implement - ✅ Fast builds (no installation overhead)

Cons: - ❌ Fragile (PYTHONPATH can be overridden) - ❌ Not standard Python practice - ❌ Doesn't work in all contexts - ❌ Hard to debug when it fails - ❌ Not portable across environments

Verdict: ❌ Avoid - This is the source of current issues.


Approach: Install shared packages as proper Python packages in builder stage.

Pattern:

# Stage 1: Builder
FROM python:3.11-slim AS builder
WORKDIR /build

# Copy workspace files
COPY pyproject.toml uv.lock ./

# Copy shared packages with their pyproject.toml files
COPY loan_defenders_models/ ./loan_defenders_models/
COPY loan_defenders_utils/ ./loan_defenders_utils/

# Copy application package config
COPY apps/api/ ./apps/api/

# Install all dependencies INCLUDING shared packages into venv
# uv workspace will resolve and install shared packages properly
RUN uv sync --frozen --no-dev --package loan-defenders-api

# Stage 2: Runtime
FROM python:3.11-slim
WORKDIR /app

# Copy ONLY the venv (shared packages are now inside it!)
COPY --from=builder /build/.venv /app/.venv

# Copy ONLY application code (NOT shared packages - they're in venv)
COPY apps/api/loan_defenders ./loan_defenders

# No PYTHONPATH needed - packages in site-packages!
ENV PATH="/app/.venv/bin:$PATH"

CMD ["uvicorn", "loan_defenders.api.app:app", "--host", "0.0.0.0", "--port", "8000"]

Pros: - ✅ Standard Python packaging - ✅ Shared packages in venv/site-packages - ✅ No PYTHONPATH needed - ✅ Works in all contexts - ✅ Proper dependency resolution - ✅ Easy to debug

Cons: - ⚠️ Slightly longer build time (install overhead) - ⚠️ Requires proper package configuration

Verdict: ✅ Recommended - Standard, reliable, portable.


Option 3: Multi-Repo with Published Packages (Future Consideration)

Approach: Publish shared packages to private PyPI or Azure Artifacts.

Pattern:

# Publish shared packages
$ cd loan_defenders_models
$ uv build
$ uv publish --repository private-pypi

# In application Dockerfile
FROM python:3.11-slim
RUN pip install --index-url https://private-pypi.example.com loan-defenders-models loan-defenders-utils

Pros: - ✅ Clean separation of concerns - ✅ Versioned dependencies - ✅ Can upgrade shared packages independently - ✅ Standard enterprise pattern

Cons: - ❌ Requires private package registry - ❌ More infrastructure to maintain - ❌ Versioning overhead - ❌ Overkill for current project size

Verdict: 🟡 Future - Consider when team grows or packages become stable APIs.


Approach: Use git submodules for shared code.

Verdict: ❌ Avoid - Adds Git complexity, doesn't solve Python import issues.


Strategy: Proper Workspace Package Installation

Goal: Install shared packages into venv/site-packages in all environments, eliminate PYTHONPATH dependency.

Key Principles: 1. Shared packages are proper Python packages (with pyproject.toml) 2. All environments install packages the same way (via uv/pip) 3. No PYTHONPATH hacks - use site-packages 4. Build from repository root - ensure full context

Implementation Approach

1. Package Configuration Fix

Current Issue: Flat layout with packages = ["."]

Recommendation: Use explicit package specification or src layout.

Option A: Explicit Package Name (Simpler)

# loan_defenders_models/pyproject.toml
[tool.hatch.build.targets.wheel]
packages = ["loan_defenders_models"]  # Explicit package name

[tool.hatch.build.targets.editable]
packages = ["loan_defenders_models"]

Option B: Src Layout (More Standard)

loan_defenders_models/
├── pyproject.toml
└── src/
    └── loan_defenders_models/
        ├── __init__.py
        ├── application.py
        └── ...

Verdict: Use Option A for minimal changes, Option B for long-term maintainability.

2. Dockerfile Pattern Update

Principle: Install shared packages into venv, don't copy as source + PYTHONPATH.

Updated Pattern:

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Stage 1: Builder - Install ALL dependencies including shared packages
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
FROM python:3.11-slim AS builder

# Install uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv

WORKDIR /build

# Copy workspace configuration first
COPY pyproject.toml uv.lock ./

# Copy shared packages WITH their pyproject.toml files
COPY loan_defenders_models/ ./loan_defenders_models/
COPY loan_defenders_utils/ ./loan_defenders_utils/

# Copy application package configuration
COPY apps/api/pyproject.toml apps/api/README.md ./apps/api/

# Install dependencies
# This will install:
# 1. loan-defenders-models into .venv/lib/python3.11/site-packages/
# 2. loan-defenders-utils into .venv/lib/python3.11/site-packages/
# 3. loan-defenders-api dependencies
RUN uv sync --frozen --no-dev --package loan-defenders-api

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Stage 2: Runtime - Copy venv (includes shared packages), no PYTHONPATH
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
FROM python:3.11-slim

ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1

# Install runtime dependencies (curl for healthcheck)
RUN apt-get update && apt-get install -y curl && apt-get clean && rm -rf /var/lib/apt/lists/*

# Create non-root user
RUN useradd -m -u 1000 apiuser

WORKDIR /app

# Copy venv from builder (includes installed shared packages!)
COPY --from=builder --chown=apiuser:apiuser /build/.venv /app/.venv

# Copy ONLY application code
COPY --chown=apiuser:apiuser apps/api/loan_defenders ./loan_defenders

# Switch to non-root user
USER apiuser

# Add venv to PATH (NO PYTHONPATH needed!)
ENV PATH="/app/.venv/bin:$PATH"

EXPOSE 8000

HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
    CMD curl -f http://localhost:8000/health || exit 1

CMD ["uvicorn", "loan_defenders.api.app:app", "--host", "0.0.0.0", "--port", "8000"]

Key Changes: - ✅ Shared packages copied WITH pyproject.toml in builder - ✅ uv sync installs shared packages into venv/site-packages - ✅ Runtime copies ONLY venv (shared packages inside it) - ✅ NO copying of shared package source in runtime stage - ✅ NO PYTHONPATH environment variable - ✅ Standard Python import mechanism

3. Azure ACR Build Pattern

Problem: ACR builds need full repository context.

Solution: Always build from repository root with proper context.

Deployment Script Pattern:

# infrastructure/scripts/build-images.sh

#!/bin/bash
set -e

ACR_NAME="$1"
IMAGE_TAG="${2:-latest}"

echo "Building images in Azure Container Registry: $ACR_NAME"

# CRITICAL: Build from repository root with full context
cd "$(git rev-parse --show-toplevel)"

# Build API image
echo "Building loan-defenders-api..."
az acr build \
    --registry "$ACR_NAME" \
    --image "loan-defenders-api:$IMAGE_TAG" \
    --file "apps/api/Dockerfile" \
    .  # ← Build context is repo root!

# Build MCP servers
for server in application_verification document_processing financial_calculations; do
    echo "Building mcp-$server..."
    az acr build \
        --registry "$ACR_NAME" \
        --image "mcp-$server:$IMAGE_TAG" \
        --file "apps/mcp_servers/$server/Dockerfile" \
        .  # ← Build context is repo root!
done

echo "✅ All images built successfully"

Key Points: - ✅ Always run from repository root - ✅ Build context is . (repo root) - ✅ Dockerfile path is relative: apps/api/Dockerfile - ✅ Full context includes: pyproject.toml, uv.lock, shared packages

4. Local Development Setup

Goal: Ensure local development matches Docker behavior.

Setup Script (scripts/setup-dev-environment.sh):

#!/bin/bash
set -e

echo "Setting up Loan Defenders development environment..."

# Ensure we're in repo root
cd "$(git rev-parse --show-toplevel)"

# Install all workspace dependencies
echo "Installing dependencies with uv..."
uv sync

# Verify shared packages are importable
echo "Verifying shared packages installation..."
uv run python -c "import loan_defenders_models; import loan_defenders_utils; print('✅ Shared packages imported successfully')"

echo ""
echo "✅ Development environment ready!"
echo ""
echo "Next steps:"
echo "  1. Configure environment: cp .env.example .env"
echo "  2. Start services: uv run uvicorn apps.api.loan_defenders.api.app:app --reload"

Verification:

# Test imports work without PYTHONPATH
$ uv run python -c "import loan_defenders_models"
# Should succeed

# Inspect where package is installed
$ uv run python -c "import loan_defenders_models; print(loan_defenders_models.__file__)"
# Should show: .venv/lib/python3.11/site-packages/loan_defenders_models/__init__.py


Implementation Plan

Phase 1: Fix Package Configuration (No Code Changes)

Goal: Update package configuration for proper installation.

Tasks: 1. ✅ Already done: Packages have pyproject.toml files 2. ⚠️ Review needed: Check hatchling configuration 3. ⚠️ Verify: packages = ["."] vs explicit naming

Validation:

# Test package build
$ cd loan_defenders_models
$ uv build
# Should create dist/loan_defenders_models-0.1.0-py3-none-any.whl

# Test install
$ pip install dist/loan_defenders_models-0.1.0-py3-none-any.whl
$ python -c "import loan_defenders_models"
# Should succeed

Action Items: - [ ] Document current hatchling configuration behavior - [ ] Test package builds locally - [ ] Verify imports work after pip install

Phase 2: Update Dockerfiles (Code Changes Required)

Goal: Eliminate PYTHONPATH dependency, install packages properly.

Tasks: 1. Update apps/api/Dockerfile 2. Update apps/mcp_servers/*/Dockerfile (3 servers) 3. Update apps/ui/Dockerfile (if applicable)

Changes: - Remove COPY of shared package source in runtime stage - Remove PYTHONPATH environment variable - Verify venv contains installed shared packages

Testing:

# Build locally
$ docker build -f apps/api/Dockerfile -t api:test .

# Run and test imports
$ docker run --rm api:test python -c "import loan_defenders_models"
# Should succeed

# Verify PYTHONPATH not needed
$ docker run --rm -e PYTHONPATH= api:test python -c "import loan_defenders_models"
# Should still succeed

Action Items: - [ ] Update API Dockerfile - [ ] Update 3 MCP server Dockerfiles - [ ] Test Docker builds locally - [ ] Verify imports work in container

Phase 3: Azure ACR Build Script (Code Changes Required)

Goal: Ensure ACR builds have full repository context.

Tasks: 1. Create infrastructure/scripts/build-images.sh 2. Update deployment scripts to use build script 3. Document build process

Script Template: See Azure ACR Build Pattern above.

Testing:

# Test ACR build script
$ ./infrastructure/scripts/build-images.sh <acr-name> test

# Verify images in ACR
$ az acr repository list --name <acr-name> --output table

# Pull and test image
$ az acr login --name <acr-name>
$ docker pull <acr-name>.azurecr.io/loan-defenders-api:test
$ docker run --rm <acr-name>.azurecr.io/loan-defenders-api:test python -c "import loan_defenders_models"
# Should succeed

Action Items: - [ ] Create build-images.sh script - [ ] Update deployment scripts - [ ] Test ACR builds - [ ] Verify deployed containers work

Phase 4: Documentation & Verification (No Code Changes)

Goal: Document the solution and create verification checklist.

Tasks: 1. Update deployment documentation 2. Create troubleshooting guide 3. Add pre-deployment verification checklist

Documentation Updates: - docs/deployment/direct-azure-deployment.md - Update build instructions - docs/getting-started/local-development.md - Update setup instructions - docs/troubleshooting/module-not-found.md - New troubleshooting guide

Action Items: - [ ] Update deployment docs - [ ] Create troubleshooting guide - [ ] Document verification steps


Verification Checklist

Local Development

# 1. Clean environment
$ rm -rf .venv
$ uv sync

# 2. Verify shared packages installed
$ uv run python -c "import loan_defenders_models; print(loan_defenders_models.__file__)"
# Expected: .venv/lib/python3.11/site-packages/loan_defenders_models/__init__.py

$ uv run python -c "import loan_defenders_utils; print(loan_defenders_utils.__file__)"
# Expected: .venv/lib/python3.11/site-packages/loan_defenders_utils/__init__.py

# 3. Test imports without PYTHONPATH
$ env -u PYTHONPATH uv run python -c "import loan_defenders_models"
# Expected: Success

# 4. Run API locally
$ uv run uvicorn apps.api.loan_defenders.api.app:app --host 0.0.0.0 --port 8000
# Expected: Server starts without import errors

Docker Builds (Local)

# 1. Build API image
$ docker build -f apps/api/Dockerfile -t loan-defenders-api:test .

# 2. Verify shared packages in container
$ docker run --rm loan-defenders-api:test python -c "import loan_defenders_models; print(loan_defenders_models.__file__)"
# Expected: /app/.venv/lib/python3.11/site-packages/loan_defenders_models/__init__.py

# 3. Test without PYTHONPATH
$ docker run --rm -e PYTHONPATH= loan-defenders-api:test python -c "import loan_defenders_models"
# Expected: Success (no PYTHONPATH needed)

# 4. Run container
$ docker run -p 8000:8000 loan-defenders-api:test
# Expected: Server starts, check http://localhost:8000/health

# 5. Repeat for MCP servers
$ docker build -f apps/mcp_servers/financial_calculations/Dockerfile -t mcp-financial:test .
$ docker run --rm mcp-financial:test python -c "import loan_defenders_models"
# Expected: Success

Azure ACR Builds

# 1. Build in ACR
$ az acr build \
    --registry <acr-name> \
    --image loan-defenders-api:test \
    --file apps/api/Dockerfile \
    .

# 2. Verify build succeeded
$ az acr repository show --name <acr-name> --image loan-defenders-api:test
# Expected: Image details returned

# 3. Pull and test image
$ az acr login --name <acr-name>
$ docker pull <acr-name>.azurecr.io/loan-defenders-api:test
$ docker run --rm <acr-name>.azurecr.io/loan-defenders-api:test python -c "import loan_defenders_models"
# Expected: Success

# 4. Test in ACI (optional)
$ az container create \
    --resource-group <rg-name> \
    --name test-api \
    --image <acr-name>.azurecr.io/loan-defenders-api:test \
    --registry-login-server <acr-name>.azurecr.io \
    --registry-username <username> \
    --registry-password <password> \
    --ports 8000

$ az container logs --resource-group <rg-name> --name test-api
# Expected: No ModuleNotFoundError, server starts successfully

Automated Verification Script

#!/bin/bash
# scripts/verify-shared-packages.sh

set -e

echo "🔍 Verifying shared package installation across environments..."

# Function to check import
check_import() {
    local context="$1"
    local command="$2"

    echo -n "  [$context] Testing import... "
    if eval "$command" &>/dev/null; then
        echo "✅"
        return 0
    else
        echo "❌"
        return 1
    fi
}

# Test local
echo "📦 Local Development:"
check_import "uv run" "uv run python -c 'import loan_defenders_models'"
check_import "no PYTHONPATH" "env -u PYTHONPATH uv run python -c 'import loan_defenders_models'"

# Test Docker
echo "🐳 Docker:"
check_import "API container" "docker run --rm loan-defenders-api:test python -c 'import loan_defenders_models'"
check_import "no PYTHONPATH" "docker run --rm -e PYTHONPATH= loan-defenders-api:test python -c 'import loan_defenders_models'"

echo ""
echo "✅ All verification checks passed!"

Summary

Current Issues

  1. PYTHONPATH Dependency: Fragile, non-standard, breaks in some contexts
  2. Inconsistent Installation: Editable installs locally, COPY in Docker
  3. ACR Build Context: Missing shared packages when building from subdirectory
  4. Package Configuration: Flat layout can cause discovery issues
  1. Proper Package Installation: Install shared packages into venv/site-packages
  2. No PYTHONPATH: Use standard Python import mechanism
  3. Consistent Pattern: Same installation method across all environments
  4. Full Build Context: Always build from repository root

Implementation Timeline

  • Phase 1 (Review): 1-2 hours (no code changes)
  • Phase 2 (Dockerfiles): 2-3 hours (5 Dockerfiles to update)
  • Phase 3 (ACR Script): 1-2 hours (new build script)
  • Phase 4 (Documentation): 2-3 hours (docs + verification)

Total: 6-10 hours to eliminate all module not found issues permanently.

Success Criteria

  • ✅ Imports work in all environments without PYTHONPATH
  • ✅ Docker builds succeed locally and in ACR
  • ✅ Deployed containers start without module errors
  • ✅ Local development matches production behavior
  • ✅ No more "module not found" issues

References

Python Packaging

uv Documentation

Docker Best Practices

Azure Container Registry

Last Updated: 2025-10-25 Review Frequency: After any deployment failures or package structure changes Next Review: After implementing Phase 2-3 updates