Thanks to visit codestin.com
Credit goes to github.com

Skip to content

junhyeong9812/python-di-container

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mini-di

Python 표준 라이브러리(inspect)만으로 만든 학습용 미니 DI(Dependency Injection) 컨테이너. Spring·FastAPI·Dagger 같은 DI 프레임워크가 내부에서 어떻게 동작하는지 따라치며 익히는 프로젝트.


핵심 기능

  • @injectable 데코레이터 — 클래스를 임포트 시점에 자동 등록.
  • 타입 힌트 기반 의존성 주입__init__ 시그니처를 inspect로 읽어 자동 해결.
  • 두 가지 스코프Scope.SINGLETON(기본, 캐싱) / Scope.PROTOTYPE(매번 새 인스턴스).
  • 순환 의존성 검출 — 경로 추적 + 위상정렬 두 단계로 안전 검증.
  • 위상정렬(Kahn's algorithm) — 의존성 그래프의 인스턴스화 순서 결정.
  • 패키지 자동 스캔bootstrap("my_app") 한 줄로 패키지 트리를 임포트해 등록.

외부 의존성 0개 (Python 3.10+ 표준 라이브러리만 사용).


설치

git clone https://github.com/junhyeong9812/python-di-container.git
cd python-di-container
pip install -e .

빠른 시작

from mini_di import injectable, Container, Scope


@injectable
class UserRepository:
    def find(self, user_id: int) -> dict:
        return {"id": user_id, "name": "alice"}


@injectable
class UserService:
    def __init__(self, repo: UserRepository) -> None:
        self.repo = repo

    def get_user(self, user_id: int) -> dict:
        return self.repo.find(user_id)


container = Container()
service = container.resolve(UserService)     # UserRepository 자동 주입
print(service.get_user(1))                    # {'id': 1, 'name': 'alice'}

bootstrap으로 패키지 자동 스캔

from mini_di import bootstrap

container = bootstrap("my_app")               # my_app 패키지 트리 전체를 스캔 → 자동 등록
service = container.resolve(MyService)

디렉토리 구조

python-di-container/
├── docs/
│   ├── notes.md         ← inspect 모듈과 DI 컨테이너 동작 원리 정리
│   ├── design.md        ← 설계 결정 (요구/제약/구조/API)
│   ├── phases.md        ← Phase 0~8 따라치기 가이드
│   ├── progress.md      ← 진행 기록
│   └── study/           ← Python 문법/패턴 주제별 심화 학습 문서
│       ├── decorator-syntax.md
│       ├── inspect-module.md
│       ├── type-hints-runtime.md
│       ├── enum-class.md
│       ├── dataclass-frozen.md
│       ├── keyword-only-args.md
│       ├── dynamic-import.md
│       └── relative-import.md
├── src/
│   └── mini_di/
│       ├── __init__.py     ← 공개 API 재노출
│       ├── scopes.py       ← Scope enum
│       ├── exceptions.py   ← DIError, NotRegisteredError 등
│       ├── registry.py     ← 전역 등록소 + ComponentMeta
│       ├── decorators.py   ← @injectable
│       ├── scanner.py      ← 패키지 자동 스캔
│       └── container.py    ← Container, resolve, topological_order, bootstrap
├── examples/
│   └── simple_app/
│       ├── repositories.py
│       ├── services.py
│       └── main.py
├── tests/
│   ├── conftest.py         ← pytest 격리 fixture
│   ├── test_resolve.py     ← 의존성 주입 동작
│   ├── test_scope.py       ← SINGLETON/PROTOTYPE
│   ├── test_cycle.py       ← 순환 의존성 검출
│   └── test_scanner.py     ← bootstrap end-to-end
└── pyproject.toml

동작 흐름

[등록]              → [의존성 그래프 분석]    → [위상 정렬]      → [인스턴스화]    → [컨테이너 보관]    → [주입]
  @injectable           inspect.signature        Kahn's algorithm   재귀 resolve     dict 캐시          타입 키로 조회
  1. 등록: @injectable이 임포트 시점에 클래스를 전역 _registry에 추가.
  2. 그래프 분석: 각 클래스의 __init__ 타입 힌트를 typing.get_type_hints로 읽어 의존 관계 파악.
  3. 위상정렬: Kahn's algorithm으로 인스턴스화 순서 결정 + 사이클 검출.
  4. 인스턴스화: SINGLETON은 한 번 만들어 캐시, PROTOTYPE은 매번 새로 생성.
  5. 주입: 같은 타입을 다시 요청하면 캐시된 동일 참조 반환.

예제 실행

python -m examples.simple_app.main
# {'id': 1, 'name': 'alice'}

테스트

pip install pytest
python -m pytest tests/ -v
# 18 passed
테스트 파일 검증 항목
test_resolve.py 의존성 없는 resolve, 자동 주입, 2-depth 체인, 미등록/타입힌트 누락/기본값 (6 cases)
test_scope.py SINGLETON 캐싱, PROTOTYPE 매번 새, 두 소비자 간 공유, default scope (4 cases)
test_cycle.py self-loop, A↔B 사이클(resolve/topological), 정상 위상순서 (4 cases)
test_scanner.py bootstrap 반환, end-to-end resolve, eager/lazy 차이 (4 cases)

학습 자료

이 프로젝트는 따라치며 익히는 학습 목적으로 만들어졌다.

  1. docs/notes.mdinspect 모듈과 DI 컨테이너의 일반 동작 원리.
  2. docs/design.md — 설계 결정 (왜 이렇게 만들었는지).
  3. docs/phases.md — Phase 0~8 따라치기 가이드. 각 줄에 "이 코드가 파이썬에서 어떤 역할인지" 1줄 주석.
  4. docs/study/@ 데코레이터, MRO, Enum 등 사용된 Python 문법/표준 라이브러리 기능을 주제별로 심화 정리. CPython 내부 동작까지 포함.

비범위 (Out of Scope)

학습 목적상 단순함을 우선하여 다음은 다루지 않는다:

  • HTTP request/session 스코프
  • Optional[X], Union[X, Y], list[X] 등 제네릭/유니온 타입 해석
  • 인터페이스(추상 클래스) → 구현체 바인딩
  • 비동기 팩토리 (async def __init__)
  • 설정 파일/환경 변수 주입
  • 메서드 인젝션 (FastAPI Depends 스타일)
  • 멀티스레드 락

확장 아이디어는 docs/phases.md 부록 C 참조.


Python 버전

  • Python 3.10+ (PEP 604 X | None 문법, dataclass(kw_only=True))

라이선스

학습 목적의 개인 프로젝트. 자유롭게 참고/수정 가능.

About

파이썬의 의존성 컨테이너 동작 구조화

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages