[pytest] 캐시 디렉터리 생성 로직 단순화 — 원자적 생성 함수 추출
PR 링크: pytest-dev/pytest#14121 상태: Merged | 변경: +76 / -49
들어가며
pytest는 .pytest_cache 디렉터리를 생성할 때 TemporaryDirectory로 임시 디렉터리를 만들고, 보조 파일(README.md, .gitignore, CACHEDIR.TAG)을 작성한 후 원자적으로 rename합니다. 기존 코드는 _ensure_cache_dir_and_supporting_files() 메서드 안에 모든 로직이 인라인되어 있었고, TemporaryDirectory의 정리 로직이 rename 후 빈 디렉터리를 다시 만들어야 하는 workaround를 포함했습니다.
핵심 코드 분석
1. _make_cachedir() 함수 추출
Before (cacheprovider.py):
def _ensure_cache_dir_and_supporting_files(self) -> None:
if self._cachedir.is_dir():
return
self._cachedir.parent.mkdir(parents=True, exist_ok=True)
with tempfile.TemporaryDirectory(prefix="pytest-cache-files-", dir=self._cachedir.parent) as newpath:
path = Path(newpath)
# ... 파일 작성 ...
try:
path.rename(self._cachedir)
except OSError as e:
if e.errno not in (errno.ENOTEMPTY, errno.EEXIST):
raise
else:
# TemporaryDirectory cleanup이 실패하지 않도록 빈 디렉터리 재생성
path.mkdir()
After:
def _make_cachedir(target: Path) -> None:
target.parent.mkdir(parents=True, exist_ok=True)
path = Path(tempfile.mkdtemp(prefix="pytest-cache-files-", dir=target.parent))
try:
umask = os.umask(0o022)
os.umask(umask)
path.chmod(0o777 - umask)
for name, content in CACHEDIR_FILES.items():
path.joinpath(name).write_bytes(content)
path.rename(target)
except OSError as e:
if e.errno not in (errno.ENOTEMPTY, errno.EEXIST):
raise
finally:
shutil.rmtree(path, ignore_errors=True)
2. 보조 파일을 딕셔너리로 통합
Before:
README_CONTENT = """\
# pytest cache directory #
..."""
CACHEDIR_TAG_CONTENT = b"""\
Signature: 8a477f597d28d172789f06886806bc55
..."""
After:
CACHEDIR_FILES: dict[str, bytes] = {
"README.md": b"# pytest cache directory #\n...",
".gitignore": b"# Created by pytest automatically.\n*\n",
"CACHEDIR.TAG": b"Signature: 8a477f597d28d172789f06886806bc55\n...",
}
세 개의 상수가 하나의 딕셔너리로 통합되어, 반복문으로 일괄 작성합니다.
3. finally 블록으로 정리 보장
TemporaryDirectory 컨텍스트 매니저 대신 mkdtemp() + finally: shutil.rmtree()를 사용합니다. rename이 성공하면 원본이 이동되어 rmtree는 ENOENT로 조용히 실패하고, rename이 실패하거나 예외가 발생하면 임시 디렉터리를 확실히 정리합니다. KeyboardInterrupt 같은 BaseException에서도 정리가 보장됩니다.
왜 이게 좋은가
TemporaryDirectory의 rename 후 빈 디렉터리 재생성 workaround가 불필요해졌습니다.finally블록으로BaseException(KeyboardInterrupt 등)에서도 임시 디렉터리가 정리됩니다.- 독립 함수로 추출하여 단위 테스트가 가능해졌습니다.
정리
- 임시 리소스는 finally에서 정리하라:
TemporaryDirectory의 컨텍스트 매니저보다mkdtemp()+finally조합이 더 유연한 정리를 제공할 수 있습니다. - 관련 상수는 하나의 자료구조로 묶어라: 개별 상수를 딕셔너리로 통합하면 일괄 처리가 간결해집니다.
참고 자료
- pytest-dev/pytest#14121 — PR 전체 diff
- CACHEDIR.TAG 사양 — 캐시 디렉터리 태그 표준
⚠️ 알림: 이 분석은 AI가 실제 코드 diff를 기반으로 작성했습니다.
관련 포스트
PR Analysis 의 다른글
- 이전글 [Open WebUI] users.py의 5개 업데이트 메서드에서 중복 SELECT 쿼리 제거
- 현재글 : [pytest] 캐시 디렉터리 생성 로직 단순화 — 원자적 생성 함수 추출
- 다음글 [Grafana Loki] JSON 파서에서 bytes.Runes() 할당을 in-place UTF-8 디코딩으로 제거
댓글