S-CANVAS (Saman Corp.) — DXF + DEM + AI 기반 3D 조감도 생성 엔진. ~24k LOC Python (scanvas_maker.py 7072 LOC GUI + 구조물 파서/빌더 다수). 이 커밋은 7-iter cleanup이 적용된 상태로 import: - F821 8 + B023 6: 비동기 lambda + except/loop 변수 캡처 NameError (Py3.13에서 reproduce 확인된 진짜 버그) - RUF012 4 + RUF013 1: ClassVar / implicit Optional 명시화 - F811/B905/B904/F401/F841/W293/F541/UP/SIM/RUF/PLR 700+ cleanup/modernization 신규 파일: - ruff.toml: target=py313, Korean unicode/저자 스타일/도메인 복잡도 무력화 - requirements-py313.txt: pyproj>=3.7, scipy>=1.14, numpy>=2.0.2 (Py3.13 wheel) - .gitignore: gcp-key.json, 캐시, 백업, 생성 이미지 제외 검증: ruff 0 errors, py_compile 0 errors, import 33/33 OK on Py3.13.13. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
138 lines
4.4 KiB
Python
138 lines
4.4 KiB
Python
# -*- mode: python ; coding: utf-8 -*-
|
|
"""S-CANVAS PyInstaller spec — onedir 배포 빌드 설정.
|
|
|
|
빌드:
|
|
pyinstaller --clean scanvas_maker.spec
|
|
|
|
결과:
|
|
dist/S-CANVAS/ ← 통째로 zip 해서 배포
|
|
S-CANVAS.exe ← 더블클릭 진입점
|
|
Design/, prompt_templates/, structure_types/
|
|
_internal/ ← Python 런타임 + 의존 라이브러리
|
|
...
|
|
|
|
런타임 데이터(DB·로그·캐시):
|
|
%LOCALAPPDATA%\\S-CANVAS\\ ← 사용자별 분리, 설치 폴더 쓰기 권한 불필요
|
|
"""
|
|
from PyInstaller.utils.hooks import collect_all, collect_submodules
|
|
from pathlib import Path
|
|
|
|
block_cipher = None
|
|
PROJECT_DIR = Path(SPECPATH).resolve()
|
|
|
|
|
|
# ────────────────────── 자산 번들링 ──────────────────────
|
|
# (소스경로, 번들 내 상대경로) — 런타임에서 sys._MEIPASS 아래에 같은 트리로 추출됨.
|
|
datas = [
|
|
(str(PROJECT_DIR / "Design"), "Design"),
|
|
(str(PROJECT_DIR / "prompt_templates"), "prompt_templates"),
|
|
(str(PROJECT_DIR / "structure_types"), "structure_types"),
|
|
]
|
|
|
|
# ────────────────────── 거대 패키지 collect_all ──────────────────────
|
|
# pyvista/vtk: PyInstaller 자동 감지 부분이 거나 → 명시적 collect_all 로 누락 방지.
|
|
# pyproj: PROJ 데이터(proj.db) 자동 누락 빈번 → collect_all 로 datas/binaries 모두 집어 옴.
|
|
binaries = []
|
|
hiddenimports = []
|
|
for pkg in [
|
|
"pyvista",
|
|
"vtkmodules",
|
|
"pyproj",
|
|
"ezdxf",
|
|
"tkintermapview",
|
|
"structlog",
|
|
"customtkinter",
|
|
"PIL",
|
|
]:
|
|
try:
|
|
_d, _b, _h = collect_all(pkg)
|
|
datas += _d
|
|
binaries += _b
|
|
hiddenimports += _h
|
|
except Exception:
|
|
pass
|
|
|
|
# google-genai 의 서브모듈은 collect_all 로 충분히 커버되지 않을 때가 있어 별도 추가
|
|
hiddenimports += collect_submodules("google.genai")
|
|
hiddenimports += [
|
|
"sqlalchemy.dialects.sqlite",
|
|
"sqlalchemy.dialects.sqlite.pysqlite",
|
|
"sqlalchemy.ext.declarative",
|
|
"scipy.spatial.transform._rotation_groups",
|
|
"scipy.special.cython_special",
|
|
"encodings.idna",
|
|
]
|
|
|
|
# ────────────────────── 분석 단계 ──────────────────────
|
|
a = Analysis(
|
|
["scanvas_maker.py"],
|
|
pathex=[str(PROJECT_DIR)],
|
|
binaries=binaries,
|
|
datas=datas,
|
|
hiddenimports=hiddenimports,
|
|
hookspath=[],
|
|
hooksconfig={},
|
|
runtime_hooks=[],
|
|
# 번들 크기 절감: 불필요한 거대 패키지 제외
|
|
excludes=[
|
|
"pytest",
|
|
"IPython",
|
|
"jupyter",
|
|
"notebook",
|
|
"tornado",
|
|
"zmq",
|
|
"matplotlib.tests",
|
|
"numpy.tests",
|
|
"scipy.tests",
|
|
"pyvista.examples", # 거대 샘플 데이터 제외
|
|
"vtkmodules.test",
|
|
],
|
|
win_no_prefer_redirects=False,
|
|
win_private_assemblies=False,
|
|
cipher=block_cipher,
|
|
noarchive=False,
|
|
)
|
|
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
|
|
|
# ────────────────────── 실행파일 ──────────────────────
|
|
# 아이콘: 빌드 전 build.bat 가 cache/icons/scanvas_S.ico 를 생성. 없으면 None 폴백.
|
|
_icon_path = PROJECT_DIR / "cache" / "icons" / "scanvas_S.ico"
|
|
_exe_icon = str(_icon_path) if _icon_path.exists() else None
|
|
|
|
exe = EXE(
|
|
pyz,
|
|
a.scripts,
|
|
[],
|
|
exclude_binaries=True,
|
|
name="S-CANVAS",
|
|
debug=False,
|
|
bootloader_ignore_signals=False,
|
|
strip=False,
|
|
upx=True, # UPX 가 PATH 에 있으면 압축. 없어도 빌드 진행.
|
|
upx_exclude=[
|
|
"vcruntime140.dll",
|
|
"python311.dll",
|
|
"python312.dll",
|
|
"VCRUNTIME140.dll",
|
|
],
|
|
console=False, # GUI 앱 — 콘솔 창 안 띄움
|
|
disable_windowed_traceback=False,
|
|
argv_emulation=False,
|
|
target_arch=None,
|
|
codesign_identity=None,
|
|
entitlements_file=None,
|
|
icon=_exe_icon,
|
|
)
|
|
|
|
# ────────────────────── onedir 패키지 ──────────────────────
|
|
coll = COLLECT(
|
|
exe,
|
|
a.binaries,
|
|
a.zipfiles,
|
|
a.datas,
|
|
strip=False,
|
|
upx=True,
|
|
upx_exclude=[],
|
|
name="S-CANVAS",
|
|
)
|