워크스페이스에서 커스텀으로 정의한 모듈들을 라이노 파이썬 전역 런타임에 로드시켜서
from src.utility import core와 같이 스크립트에서 직접 불러올 수 있게 해줌.
- 파일 삽입 위치 : C:\Users\UserName\.rhinocode\py39-rh8\Lib
- Rhino8부터 가능...
"""
Dev auto-reloader for your in-tree packages on Rhino's shared Python.
- Watches given package prefixes and forces reload if sources changed.
- No change needed in GH scripts: keep 'from src.utility.mapper import ...'.
If 필요 패키지명/경로를 바꾸려면 PACKAGES / ROOTS 를 수정하세요.
"""
import builtins, importlib, importlib.util, os, sys
from pathlib import Path
# === 설정 ===
# 작업 트리의 루트/패키지 목록
ROOTS = [
Path(r"C:\Users\admin\Desktop\KH\work\GHtoPython"), # <-- src 루트
]
PACKAGES = ("src",) # prefix 매칭
# 루트 경로를 sys.path 최우선에 올림
for root in ROOTS:
p = str(root)
if root.exists() and p not in sys.path:
sys.path.insert(0, p)
# === 오토리로드 구현 ===
_mtime_cache = {} # module_name -> last_mtime
_original_import = builtins.__import__
def _module_files_under(prefix: str):
"""sys.modules 중 prefix로 시작하는 모듈의 .__file__ 경로들을 나열."""
for name, mod in list(sys.modules.items()):
if not (name == prefix or name.startswith(prefix + ".")):
continue
path = getattr(mod, "__file__", None)
if path and os.path.exists(path):
yield name, path
def _package_changed(prefix: str) -> bool:
"""패키지 트리 내부에서 더 최신 mtime이 발견되면 True."""
changed = False
for name, path in _module_files_under(prefix):
try:
mtime = os.path.getmtime(path)
except OSError:
continue
old = _mtime_cache.get(name)
if old is None or mtime > old + 1e-6: # NTFS timestamp 여유
_mtime_cache[name] = mtime
changed = True
return changed
def _purge(prefix: str):
"""해당 패키지/하위 모듈을 sys.modules에서 제거(깊은 것부터)."""
victims = [n for n in sys.modules if n == prefix or n.startswith(prefix + ".")]
for n in sorted(victims, key=lambda s: s.count("."), reverse=True):
sys.modules.pop(n, None)
def _dev_import(name, globals=None, locals=None, fromlist=(), level=0):
# 원래 동작 전에 지정한 패키지들만 검사
full = name
if level and globals and "__package__" in globals:
# 상대 import는 절대경로로 해석
pkg = globals.get("__package__") or ""
full = importlib.util.resolve_name(name, pkg)
for prefix in PACKAGES:
if full == prefix or full.startswith(prefix + "."):
# 파일이 바뀌었으면 캐시 삭제 → 이후 import가 새로 로드
if _package_changed(prefix):
_purge(prefix)
break
return _original_import(name, globals, locals, fromlist, level)
# 한 번만 설치
if not getattr(sys, "_dev_autoreload_installed", False):
builtins.__import__ = _dev_import
sys._dev_autoreload_installed = True