개발 가이드 (한국어)¶
🌐 DEVELOPMENT.md의 번역입니다. 영어 원문이 표준이며, 페이지 번역은 경고 수준의 동기화 규칙을 따릅니다.
개발 환경 설정, 테스트 실행, sqlalchemy-cubrid 기여에 필요한 모든 것.
목차¶
사전 준비¶
| 요구사항 | 버전 |
|---|---|
| Python | 3.10+ |
| Git | 아무 버전 |
| Docker | 아무 버전 (통합 테스트용) |
| Docker Compose | v2+ |
설치¶
빠른 설정¶
make install이 수행하는 것:
1. pip install -e ".[dev]" — dev 의존성과 함께 편집 가능 설치
2. pip install pytest-cov pre-commit tox — 테스트 도구
3. pre-commit install — git 훅 설정
수동 설정¶
# 가상 환경 생성 및 활성화
python3 -m venv venv
source venv/bin/activate # Linux/macOS
# venv\Scripts\activate # Windows
# dev 의존성과 함께 편집 가능 모드로 설치
pip install -e ".[dev]"
# 테스트 커버리지 및 다중 버전 도구 설치
pip install pytest-cov tox
# (선택) pre-commit 훅 설치
pip install pre-commit
pre-commit install
프로젝트 구조¶
graph TD
root["sqlalchemy-cubrid/"]
pkg["sqlalchemy_cubrid/ - Main package"]
tests["test/ - Test suite"]
docs["docs/ - Documentation"]
samples["samples/ - Usage examples"]
pyproject["pyproject.toml - Project config, dependencies"]
tox["tox.ini - Multi-Python test config"]
docker["docker-compose.yml - CUBRID Docker setup"]
makefile["Makefile - Development shortcuts"]
contributing["CONTRIBUTING.md - Contribution guidelines"]
root --> pkg
root --> tests
root --> docs
root --> samples
root --> pyproject
root --> tox
root --> docker
root --> makefile
root --> contributing
pkg --> init["__init__.py - Public API, version, type exports"]
pkg --> base["base.py - ExecutionContext, IdentifierPreparer"]
pkg --> compiler["compiler.py - SQL/DDL/Type compilers"]
pkg --> dialect["dialect.py - CubridDialect (reflection, connection, etc.)"]
pkg --> pycubrid_dialect["pycubrid_dialect.py - Pure Python driver dialect"]
pkg --> aio_dialect["aio_pycubrid_dialect.py - Async pycubrid.aio dialect"]
pkg --> dml["dml.py - ON DUPLICATE KEY UPDATE, MERGE constructs"]
pkg --> trace["trace.py - Query tracing utility"]
pkg --> types["types.py - CUBRID type system"]
pkg --> req["requirements.py - SA 2.0 test requirement flags"]
pkg --> alembic["alembic_impl.py - Alembic migration support"]
pkg --> typed["py.typed - PEP 561 marker"]
tests --> tcomp["test_compiler.py - SQL compilation tests"]
tests --> ttypes["test_types.py - Type system tests"]
tests --> tdialect["test_dialect_offline.py - Dialect tests (no DB)"]
tests --> tbase["test_base.py - Base module tests"]
tests --> treq["test_requirements.py - SA requirement flag tests"]
tests --> tdml["test_dml.py - DML construct tests"]
tests --> talembic["test_alembic.py - Alembic integration tests"]
tests --> taio["test_aio_pycubrid_dialect.py - Async dialect tests"]
tests --> taioint["test_aio_integration.py - Async integration tests"]
tests --> tjson["test_json.py - JSON type and path tests"]
tests --> tpackaging["test_packaging.py - Packaging and entry point tests"]
tests --> tshowcreate["test_show_create_table.py - Reflection parser tests"]
tests --> ttrace["test_trace.py - Query trace tests"]
tests --> tintegration["test_integration.py - Live DB integration tests"]
tests --> tsuite["test_suite.py - SA test suite runner"]
tests --> tconftest["conftest.py - Test fixtures"]
Make 타깃¶
모든 흔한 개발 작업은 make로 사용할 수 있습니다:
make help # 사용 가능한 모든 타깃 표시
make install # 모든 의존성과 함께 개발 모드 설치
make lint # ruff 린터 + 포맷 검사 실행
make format # 린트 문제 자동 수정 및 코드 포맷
make test # 커버리지와 함께 오프라인 테스트 실행 (95% 임계값)
make test-all # 모든 Python 버전에서 tox 실행
make integration # Docker 시작 → 통합 테스트 실행 → Docker 중지
make docker-up # CUBRID Docker 컨테이너 시작
make docker-down # CUBRID Docker 컨테이너 중지 및 제거
make clean # 빌드 산출물과 캐시 제거
테스트 실행¶
오프라인 테스트 (데이터베이스 불필요)¶
대부분의 테스트 스위트는 라이브 CUBRID 인스턴스 없이 실행됩니다:
# 모든 오프라인 테스트 실행
pytest test/ -v --ignore=test/test_integration.py --ignore=test/test_suite.py \
--ignore=test/test_aio_integration.py
# 커버리지 리포트와 함께 실행
pytest test/ -v --ignore=test/test_integration.py --ignore=test/test_suite.py \
--ignore=test/test_aio_integration.py \
--cov=sqlalchemy_cubrid --cov-report=term-missing
# 특정 테스트 파일 실행
pytest test/test_compiler.py -v
# 단일 테스트 실행
pytest test/test_compiler.py::TestCubridSQLCompiler::test_select_limit -v
속성 기반 퍼즈 테스트 (Hypothesis)¶
test/test_fuzz_*.py는 Hypothesis를 사용해 손으로 작성한 스위트가 열거하지 않은 SELECT/INSERT/DDL 조합을 생성하고 dialect 불변식을 검증합니다(예상치 못한 컴파일 예외 없음, placeholder == 파라미터 개수, LIMIT/OFFSET 카디널리티, DDL/reflection 왕복). 오프라인 퍼즈 테스트는 일반 오프라인 스위트에서 실행되며, 라이브 실행 퍼즈 테스트는 integration 마커가 붙습니다.
# 빠른 프로파일 (기본, 테스트당 ~50 예제) — 오프라인 스위트와 함께 실행
pytest test/test_fuzz_select.py -v
# 확장 프로파일 (테스트당 2000 예제) — nightly 버그 헌트 프로파일
HYPOTHESIS_PROFILE=nightly pytest test/test_fuzz_select.py -v
# 라이브 실행 퍼징 (CUBRID 필요)
export CUBRID_TEST_URL="cubrid+pycubrid://dba@localhost:33000/testdb"
HYPOTHESIS_PROFILE=nightly pytest test/test_fuzz_select.py -m integration -v
프로파일(dev, ci, nightly)은 test/conftest.py에 등록되며 HYPOTHESIS_PROFILE로 선택합니다. PR CI는 빠른 프로파일을 사용하고, nightly integration-full 워크플로는 라이브 CUBRID에 대해 확장 프로파일을 실행합니다.
통합 테스트 (CUBRID 필요)¶
# CUBRID 컨테이너 시작
docker compose up -d
# CUBRID 준비 대기 (헬스체크: ~30초)
docker compose logs -f cubrid
# 연결 URL 설정
export CUBRID_TEST_URL="cubrid://dba@localhost:33000/testdb"
# 통합 테스트 실행
pytest test/test_integration.py -v
# 비동기 통합 테스트 실행
pytest test/test_aio_integration.py -v
# 컨테이너 중지
docker compose down -v
전체 SA 테스트 스위트¶
Docker 통합 테스트¶
docker-compose.yml¶
프로젝트에는 로컬 CUBRID 인스턴스를 위한 docker-compose.yml이 포함되어 있습니다:
services:
cubrid:
image: cubrid/cubrid:${CUBRID_VERSION:-11.2}
environment:
CUBRID_DB: testdb
ports:
- "33000:33000"
healthcheck:
test: ["CMD", "csql", "-u", "dba", "testdb", "-c", "SELECT 1"]
interval: 15s
timeout: 10s
retries: 10
start_period: 30s
다른 CUBRID 버전에 대한 테스트¶
# 기본 (11.2)
docker compose up -d
# 특정 버전
CUBRID_VERSION=11.4 docker compose up -d
CUBRID_VERSION=11.0 docker compose up -d
CUBRID_VERSION=10.2 docker compose up -d
지원되는 CUBRID 버전¶
| 버전 | Docker 이미지 |
|---|---|
| 11.4 | cubrid/cubrid:11.4 |
| 11.2 | cubrid/cubrid:11.2 (기본) |
| 11.0 | cubrid/cubrid:11.0 |
| 10.2 | cubrid/cubrid:10.2 |
빠른 통합 워크플로¶
다중 버전 테스트¶
tox 구성¶
tox.ini는 Python 3.10–3.13의 로컬 환경을 정의합니다. GitHub Actions도 Python 3.14에서 오프라인 스위트를 실행합니다.
tox 실행¶
CI 매트릭스¶
CI 파이프라인은 다음 매트릭스를 테스트합니다:
| Python 3.10 | Python 3.11 | Python 3.12 | Python 3.13 | Python 3.14 | |
|---|---|---|---|---|---|
| 오프라인 테스트 | ✅ | ✅ | ✅ | ✅ | ✅ |
| CUBRID 11.4 | ✅ | — | — | — | ✅ |
| CUBRID 11.2 | ✅ | — | — | — | ✅ |
| CUBRID 11.0 | ✅ | — | — | — | ✅ |
| CUBRID 10.2 | ✅ | — | — | — | ✅ |
코드 커버리지¶
요구사항¶
- 최소 임계값: 라인 커버리지 95%
- 현재 CI 오프라인 수집: 603개 테스트 (
test_integration.py,test_suite.py,test_aio_integration.py제외한pytest --collect-only—.github/workflows/ci.yml및make test와 일치) - 현재 라인 커버리지: CI/make test 구성에서 오프라인 ~98.26%
- CI는
--cov-fail-under=95로 임계값을 강제
커버리지 실행¶
# 커버리지 리포트와 함께
pytest test/ -v \
--ignore=test/test_integration.py \
--ignore=test/test_suite.py \
--ignore=test/test_aio_integration.py \
--cov=sqlalchemy_cubrid \
--cov-report=term-missing \
--cov-fail-under=95
# 또는 make로
make test
알려진 도달 불가능 라인¶
compiler.py의 세 라인과 dml.py의 한 라인은 설계상 도달 불가능으로 검증되어 있습니다 (SA 공개 API로는 발동할 수 없는 방어적 폴백):
| 파일 | 라인 | 설명 |
|---|---|---|
compiler.py |
72 | for_update_clause가 "" 반환 |
compiler.py |
84 | limit_clause가 "" 반환 |
compiler.py |
298--300 | DDL 컴파일의 방어적 분기 |
dml.py |
310 | 타입 정규화의 else 분기 |
코드 스타일¶
Ruff¶
이 프로젝트는 린팅과 포맷팅 모두에 Ruff를 사용합니다.
| 설정 | 값 |
|---|---|
| 행 길이 | 100자 |
| 대상 Python | 3.10+ |
| 린터 | ruff check |
| 포매터 | ruff format |
검사 실행¶
# 린트 검사
ruff check sqlalchemy_cubrid/ test/
# 린트 문제 자동 수정
ruff check --fix sqlalchemy_cubrid/ test/
# 포맷 검사
ruff format --check sqlalchemy_cubrid/ test/
# 포맷 적용
ruff format sqlalchemy_cubrid/ test/
# make로 전체 검사
make lint
Pre-Commit 훅¶
Pre-commit 훅은 git commit 시 린트와 포맷 검사를 자동 실행합니다.
설정¶
수동 실행¶
CI/CD 파이프라인¶
GitHub Actions 워크플로¶
| 워크플로 | 파일 | 트리거 |
|---|---|---|
| CI | .github/workflows/ci.yml |
main 푸시, PR |
| Publish | .github/workflows/publish-pypi.yml |
GitHub Release |
CI 파이프라인 단계¶
- Lint — Ruff check + 포맷 검증
- 오프라인 테스트 — Python 3.10, 3.11, 3.12, 3.13, 3.14 × 오프라인 테스트 스위트
- 통합 테스트 — Python {3.10, 3.14} × CUBRID {10.2, 11.0, 11.2, 11.4}, 비동기 통합 커버리지 포함
- 커버리지 — ≥ 95% 임계값 강제
Publish 파이프라인¶
GitHub Release 생성 시 트리거. 패키지를 빌드해 PyPI에 게시합니다.