diff --git a/app/api/schemas.py b/app/api/schemas.py index 96aa5d7..6904eb0 100644 --- a/app/api/schemas.py +++ b/app/api/schemas.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from datetime import datetime from typing import Literal diff --git a/app/core/config.py b/app/core/config.py index 88db9e9..c3cf7a9 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from functools import lru_cache from pathlib import Path diff --git a/docs/SCOPE_AND_METRICS.md b/docs/SCOPE_AND_METRICS.md index f6ebe46..6052970 100644 --- a/docs/SCOPE_AND_METRICS.md +++ b/docs/SCOPE_AND_METRICS.md @@ -69,20 +69,37 @@ | 4 | 자체 제작 실제 표절 글 vs 비표절 글을 classification 하여 precision 계산 | **Ubuntu 22.04, python 3.9**, 자체 test script (GPU 불필요) | | 7 | 요약 데이터셋 구축 후 n-gram ROUGE **recall** 계산 (수식 분모가 참조 n-gram 수). 다중 참조 전제 | **A100 GPU, python 3.9, pytorch 2.2.2+cu121, transformers 4.39.3** | -### ⚠️ python 3.9 호환성 위험 (확인 필요) +### python 3.9 호환성 — 확인 완료, 수정됨 (2026-08-19) -평가환경이 **python 3.9 고정**인데, 현재 코드 일부가 3.9에서 임포트 단계에 실패할 -가능성이 있다. +평가환경이 **python 3.9 고정**인데 실제로 **성능지표 평가 스크립트 3종이 전부 +임포트 불가**였다. Docker `python:3.9-slim` 에서 재현·수정·재검증했다. -- `app/core/config.py`, `app/api/schemas.py` 가 `from __future__ import annotations` - 없이 `float | None` (PEP 604) 문법을 쓴다. -- 두 파일 모두 Pydantic 모델이라 어노테이션이 **런타임에 평가**된다. python 3.9에는 - `X | Y` 연산자가 없어 `TypeError` 가 난다. -- 현재 개발 환경은 python 3.14 라 이 문제가 드러나지 않는다. +**원인**: `app/core/config.py`, `app/api/schemas.py` 가 `from __future__ import +annotations` 없이 `str | None` (PEP 604) 을 썼다. 3.10 부터의 문법이라 클래스 +본문 실행 시점에 인터프리터가 평가하며 `TypeError` 로 죽는다. -**해야 할 일**: python 3.9 컨테이너에서 임포트 스모크 테스트를 돌려 실제로 깨지는지 -확인하고, 깨지면 `Optional[float]` 로 바꾸거나 평가 스크립트를 본체와 분리한다. -No.4 측정 직전에 발견하면 일정이 밀린다. +**파급**: 이 두 파일을 타고 `detector` / `extractor` / `clustering` / `taxonomy` / +`jobs.store` / `routes` / `main` 이 연쇄로 깨졌고, 결과적으로 + +| 지표 | 평가 스크립트 | 3.9 임포트 경로 | +|---|---|---| +| No.7 | `eval_rouge.py` | → `summarizer` → `core.config` ❌ | +| No.3 | `eval_metadata_f1.py` | → `extractor` → `api.schemas` ❌ | +| No.4 | `evaluate_pairs.py` | → `detector` → `api.schemas` ❌ | + +개발환경이 3.14 라 로컬에서는 전혀 드러나지 않았다. + +**수정**: 두 파일에 `from __future__ import annotations` 추가 + `requirements.txt` +에 `eval_type_backport>=0.2; python_version < "3.10"` 추가. 둘 다 필요하다 — +future 임포트만 넣으면 pydantic 이 문자열 `'str | None'` 을 해석하지 못하고, +백포트만 넣으면 인터프리터가 먼저 죽는다. + +**검증**: 3.9 컨테이너에서 `eval_rouge.py` / `eval_metadata_f1.py` 가 정상 실행되고 +수치가 3.14 와 동일함을 확인했다(ROUGE-1 recall 0.5778). `evaluate_pairs.py` 와 +`app.main` 임포트도 통과. 3.14 회귀 없음(153 passed). + +**재발 방지**: `tests/test_py39_compat.py` — Pydantic 모델을 정의하는 모듈에 +future 임포트가 있는지 AST 로 검사한다. 3.14 에서도 회귀를 잡는다. ## 5. AI 생성 의심도의 위상 — 지표가 아니다 @@ -100,7 +117,7 @@ No.4 측정 직전에 발견하면 일정이 밀린다. 1. **사용자 맞춤형 요약 옵션** — 상세도, 강조 내용 (계획서 명시, 현재 없음) 2. **Human Feedback Preference Optimization** — 현재 골격만. 선호 라벨 + GPU 필요 3. **도메인별 요약 데이터셋 구축** — 컴북스 데이터 수령 후 -4. **python 3.9 호환성 정리** — 평가환경 대비 +4. ~~python 3.9 호환성 정리~~ — **완료** (2026-08-19) ### 측정해야 하는 것 (전부 데이터 대기) 1. **No.4 표절 정밀도 97%** — 자체 제작 표절/비표절 글 필요. 정밀도 지표이므로 @@ -114,5 +131,4 @@ No.4 측정 직전에 발견하면 일정이 밀린다. ### 데이터와 무관하게 지금 가능한 것 - SW 저작권 등록 1건 (서류 제출) - 사용자 맞춤형 요약 옵션 개발 -- python 3.9 호환성 확인 - 바이칼/컴북스 E2E 통합 diff --git a/requirements.txt b/requirements.txt index 09bcdfc..b8a236c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,3 +14,6 @@ joblib>=1.4 scipy>=1.13 pypdf>=5.0 python-docx>=1.1 +# 공인인증 평가환경이 python 3.9 고정이라(계획서 p.24) PEP 604 어노테이션을 +# pydantic 이 해석하려면 이 백포트가 필요하다. 3.10+ 에서는 설치되지 않는다. +eval_type_backport>=0.2; python_version < "3.10" diff --git a/tests/test_py39_compat.py b/tests/test_py39_compat.py new file mode 100644 index 0000000..4b182ec --- /dev/null +++ b/tests/test_py39_compat.py @@ -0,0 +1,84 @@ +"""공인인증 평가환경(python 3.9) 호환성 가드. + +왜 필요한가: + 계획서 p.24 는 No.3/4/7 의 평가환경을 **python 3.9** 로 못박았다. 그런데 + `str | None` (PEP 604) 은 3.10 부터의 문법이라, `from __future__ import + annotations` 없이 쓰면 클래스 본문이 실행되는 순간 인터프리터가 평가해 + TypeError 로 죽는다. Pydantic 모델은 어노테이션이 런타임에 해석되므로 + 특히 위험하다. + + 개발 환경이 3.14 라 이 문제는 로컬에서 절대 드러나지 않는다. 실제로 + `app/core/config.py` 와 `app/api/schemas.py` 가 이 상태였고, 그 두 파일을 + 타고 detector·routes·main 과 **성능지표 평가 스크립트 3종이 전부** 3.9 에서 + 임포트 불가였다(2026-08-19 확인·수정). + + 이 테스트는 3.14 에서도 그 회귀를 잡는다. 문법이 아니라 소스에 선언이 + 있는지를 본다. +""" + +from __future__ import annotations + +import ast +from pathlib import Path + +import pytest + +ROOT = Path(__file__).resolve().parent.parent +PYDANTIC_BASES = {"BaseModel", "BaseSettings"} + + +def _has_future_annotations(tree: ast.Module) -> bool: + for node in tree.body: + if isinstance(node, ast.ImportFrom) and node.module == "__future__": + if any(alias.name == "annotations" for alias in node.names): + return True + return False + + +def _defines_pydantic_model(tree: ast.Module) -> bool: + for node in ast.walk(tree): + if isinstance(node, ast.ClassDef): + for base in node.bases: + name = base.id if isinstance(base, ast.Name) else getattr(base, "attr", None) + if name in PYDANTIC_BASES: + return True + return False + + +def _pydantic_modules() -> list[Path]: + found = [] + for path in sorted((ROOT / "app").rglob("*.py")): + tree = ast.parse(path.read_text(encoding="utf-8")) + if _defines_pydantic_model(tree): + found.append(path) + return found + + +def test_pydantic_modules_exist(): + """탐지 자체가 망가지면 아래 테스트가 조용히 무력화되므로 먼저 확인한다.""" + assert _pydantic_modules(), "Pydantic 모델 모듈을 하나도 못 찾았습니다." + + +@pytest.mark.parametrize( + "path", _pydantic_modules(), ids=lambda p: str(p.relative_to(ROOT)) +) +def test_pydantic_module_defers_annotations(path: Path): + """Pydantic 모델을 정의하는 모듈은 어노테이션 평가를 미뤄야 한다.""" + tree = ast.parse(path.read_text(encoding="utf-8")) + assert _has_future_annotations(tree), ( + f"{path.relative_to(ROOT)} 에 `from __future__ import annotations` 가 " + "없습니다. PEP 604(`X | None`)를 쓰면 평가환경(python 3.9)에서 임포트가 " + "실패합니다." + ) + + +def test_eval_type_backport_pinned_for_py39(): + """3.9 에서는 __future__ 만으로 부족하고 pydantic 이 백포트를 요구한다.""" + req = (ROOT / "requirements.txt").read_text(encoding="utf-8") + assert "eval_type_backport" in req, ( + "requirements.txt 에 eval_type_backport 가 없습니다. python 3.9 에서 " + "pydantic 이 문자열 어노테이션 'str | None' 을 해석하지 못합니다." + ) + assert 'python_version < "3.10"' in req, ( + "eval_type_backport 는 3.9 전용입니다. 환경 마커를 붙이세요." + )