1 개요
Python 패키지 설치 도구가 외부 repo를 인식하려면 반드시
pyproject.toml또는setup.py가 있어야 한다. 이 파일이 없으면 pip와 Poetry 모두 설치를 거부한다. 수정 권한이 없는 외부 repo라면 PR을 통해 파일을 추가하고, 승인 대기 중에는 local path로 임시 설치하는 전략이 실용적이다.
1.1 이 글이 다루는 상황
외부 repo (pyproject.toml 없음)
↓ poetry add git+ssh://...
ERROR: Source does not appear to be a Python project
↓ 해결 방법?
pyproject.toml 설계 → PR 반영 → git URL 설치
실무에서 이 상황은 다음과 같은 경우에 자주 발생한다:
- 데이터 분석/연구 목적으로 만들어진 repo (패키징 고려 없이 작성)
- 오래된 legacy repo (setup.py도 없는 경우)
- archive 상태 repo (직접 push 불가)
- 내가 관리하지 않는 조직 repo (PR을 통해서만 기여 가능)
2 Python 패키지화의 일반 원칙
2.1 패키지로 인식되기 위한 조건
pip, Poetry 등 Python 패키지 설치 도구는 아래 파일 중 하나가 있어야 패키지로 인식한다.
| 파일 | 방식 | 비고 |
|---|---|---|
pyproject.toml |
현대적 표준 (PEP 517/518) | Poetry, setuptools, Hatch 등 지원 |
setup.py |
구식 방식 | 여전히 동작하지만 deprecated 추세 |
setup.cfg |
setup.py의 선언형 버전 |
단독으로는 불충분, setup.py 필요 |
이 파일들이 없으면:
Source does not appear to be a Python project:
no pyproject.toml or setup.py
2.2 표준 Python 패키지 디렉토리 구조
패키지 설치 도구가 기대하는 구조는 아래와 같다:
repo/
├── pyproject.toml ← 필수
├── README.md
├── src/
│ └── my_package/ ← src layout (권장)
│ ├── __init__.py
│ └── module.py
└── tests/
또는 flat layout:
repo/
├── pyproject.toml
├── my_package/ ← flat layout
│ ├── __init__.py
│ └── module.py
└── tests/
2.3 비표준 디렉토리명 처리
디렉토리명이 source/, lib/, code/ 등 비표준인 경우 [tool.setuptools.package-dir]로 매핑한다:
이 설정이 없으면 setuptools는 해당 디렉토리를 패키지로 인식하지 못한다.
2.4 배포 이름 vs import 이름
| 구분 | 예시 | 사용처 |
|---|---|---|
| 배포 이름 (distribution name) | sg-data-standardization |
pip install, pyproject.toml의 name |
| import 이름 (package name) | data_standardization |
import data_standardization |
두 이름이 달라도 되지만, pyproject.toml에서 명확히 매핑해야 한다.
3 실전 사례: data_standardization repo 패키지화
조직 내부 repo에
pyproject.toml이 없어 Poetry로 설치가 불가한 상황. repo가 archive 상태라 직접 push도 불가하다. PR을 통해 파일을 추가하면서, 승인 대기 중에는 local path로 임시 설치한다.
3.1 대상 repo 구조 파악
먼저 repo를 clone해서 실제 구조를 확인한다.
git clone git@seegene_org:SeegeneDevelopmentPlatform/data_standardization.git /tmp/ds-check
ls /tmp/ds-check/README.md current_analysis data_hierarchy output standardization_rules
css data domain_dictionary source term_dictionary
source/ 디렉토리에 실제 Python 코드가 있다.
__init__.py readme.md troubleshooter.py
abbreviation_manager.py report_generator.py update_physical_names.py
completeness_analyzer.py rule_analyzer.py vocabulary_analyzer.py
data_loader.py token_analysis.py
output token_processor.py
일반적인 Python 패키지 구조라면 src/패키지명/ 또는 패키지명/이 루트에 있어야 한다. 이 repo는 source/라는 비표준 디렉토리명을 사용하고 있어 [tool.setuptools.package-dir]로 매핑이 필요하다.
3.2 __init__.py 내용 확인
패키지 이름과 공개 API를 파악하기 위해 __init__.py를 확인한다.
# source/__init__.py
from .abbreviation_manager import AbbreviationManager
from .token_processor import TokenProcessor
from .rule_analyzer import RuleAnalyzer
from .data_loader import DataLoader
# ...
__all__ = ["AbbreviationManager", "TokenProcessor", ...]
__version__ = "1.0.0"import data_standardization 시 이 클래스들이 노출된다.
4 pyproject.toml 설계
4.1 결정해야 할 항목
| 항목 | 결정 | 이유 |
|---|---|---|
| 빌드 백엔드 | setuptools |
기존 코드 변경 최소화, 범용성 |
| 배포 이름 | sg-data-standardization |
조직 prefix로 충돌 방지 |
| import 이름 | data_standardization |
간결하고 직관적 |
| 패키지 경로 | source/ → data_standardization 매핑 |
기존 디렉토리명 유지 |
| Python 버전 | >=3.11 |
프로젝트 최소 버전 |
4.2 최종 pyproject.toml 작성
[build-system]
requires = ["setuptools>=68", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "sg-data-standardization"
version = "1.0.0"
description = "데이터베이스 테이블/컬럼 명명 규칙 검증 및 물리명 자동 생성 패키지"
readme = "source/readme.md"
requires-python = ">=3.11"
license = { text = "Proprietary" }
authors = [
{ name = "김광민" },
]
dependencies = [
"pandas>=1.5.0",
"openpyxl>=3.0.0",
"tqdm>=4.64.0",
]
[project.optional-dependencies]
analysis = [
"numpy>=1.20.0",
"matplotlib>=3.5.0",
"seaborn>=0.11.0",
]
# 핵심: source/ 디렉토리를 data_standardization 이름으로 노출
[tool.setuptools]
packages = ["data_standardization"]
[tool.setuptools.package-dir]
"data_standardization" = "source"
[tool.setuptools.package-data]
"data_standardization" = ["*.md", "*.txt"]4.3 [tool.setuptools.package-dir] 동작 방식
repo 실제 구조: Python에서 보이는 구조:
source/ → data_standardization/
__init__.py __init__.py
abbreviation_manager.py abbreviation_manager.py
... ...
5 PR을 통한 pyproject.toml 반영 흐름
5.1 archive 상태 repo의 경우
repo가 archive 상태이면 직접 push가 불가하다.
archive는 GitHub에서 repo를 읽기 전용으로 만드는 기능이다. 관리자에게 unarchive를 요청한 후 PR 흐름을 진행한다.
5.2 전체 PR 흐름
1. repo 관리자에게 unarchive 요청
↓
2. feature 브랜치 생성
git checkout -b docs/packaging
↓
3. pyproject.toml 추가 및 commit
git add pyproject.toml
git commit -m "feat: add pyproject.toml for pip/poetry packaging"
↓
4. remote에 push
git push origin docs/packaging
↓
5. PR 생성 → 코드 리뷰 → main 브랜치에 merge
↓
6. 소비자 프로젝트에서 poetry install로 git URL 설치 완료
6 PR 승인 전 임시 local path 설치
PR 승인을 기다리는 동안 개발을 멈출 필요 없다. local clone에 pyproject.toml을 직접 추가하고 local path 의존성으로 임시 설치한다.
# local clone에 pyproject.toml 추가 (untracked 파일로 존재)
cp pyproject.toml /home/azureuser/projects/data_standardization/
# agent 프로젝트에서 local path로 설치
cd /home/azureuser/projects/kmkim/agent
poetry add /home/azureuser/projects/data_standardizationpyproject.toml의 의존성 항목:
# PR 승인 전 (local path)
"sg-data-standardization @ file:///home/azureuser/projects/data_standardization"설치 확인:
7 PR 승인 후 git URL로 전환
PR이 merge되어 remote에 pyproject.toml이 반영되면 file:// 경로를 git URL로 교체한다.
# PR 승인 후 (git URL)
"sg-data-standardization @ git+ssh://git@seegene_org/SeegeneDevelopmentPlatform/data_standardization.git@main"출력:
- Updating sg-data-standardization
(1.0.0 /home/.../data_standardization -> 1.0.0 f3354b9)
local path에서 remote commit(f3354b9)으로 전환 완료다.
8 git pull 시 충돌 주의
local clone에 untracked 파일(pyproject.toml)이 있는 상태에서 remote에 같은 파일이 추가되면 git pull이 실패한다.
error: The following untracked working tree files would be overwritten by merge:
pyproject.toml
Please move or remove them before you merge.
Aborting
해결:
이후 remote의 pyproject.toml이 자동으로 내려온다.
9 요약
| 항목 | 내용 |
|---|---|
| 패키지화 조건 | pyproject.toml 또는 setup.py 필요 |
| 비표준 디렉토리 매핑 | [tool.setuptools.package-dir]로 해결 |
| 배포 이름 vs import 이름 | name 필드와 package-dir 키로 분리 관리 |
| archive repo 우회 | unarchive 요청 → PR → merge 흐름 |
| 개발 연속성 | PR 승인 전 local path로 임시 설치 |
| 전환 방법 | file:// → git+ssh:// 교체 후 poetry lock |
| git pull 충돌 | untracked 파일 제거 후 pull |
다음 블로그에서는 local path에서 git URL로 전환하는 전체 흐름과 poetry.lock에서 올바르게 설치됐는지 검증하는 방법을 다룬다.