PEP 369 – 사후 임포트 훅
- Author:
- Christian Heimes <christian at python.org>
- Status:
- Withdrawn
- Type:
- Standards Track
- Created:
- 02-Jan-2008
- Python-Version:
- 2.6, 3.0
- Post-History:
- 02-Dec-2012
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
철회 공지
이 PEP는 작성자가 철회했습니다. Python 3.3에서 importlib로 이전한 이후 세부 설계의 상당 부분이 더 이상 유효하지 않기 때문입니다.
초록
이 PEP는 사후 임포트 훅을 추가하기 위한 임포트 메커니즘 개선을 제안합니다. 이는 주로 Python 3.0에서 예상되는 추상 베이스 클래스의 폭넓은 사용을 지원하기 위한 것입니다.
이 PEP는 원래 지연 임포트와 사후 임포트 훅을 함께 다루는 통합 PEP로 시작되었습니다. python-dev 메일링 리스트에서 논의를 거친 후 이 PEP는 두 개의 별도 PEP로 나뉘었습니다. [1]
근거
Python에는 임포트 메커니즘에 훅을 걸어 모듈이 성공적으로 로드된 후에 코드를 실행하는 API가 없습니다. PEP 302의 임포트 훅은 모듈을 찾고 로드하는 것에 관한 것이며, 임포트 후 훅으로 사용하도록 설계되지 않았습니다.
사용 사례
사후 임포트 훅의 사용 사례는 모듈 임포트 시의 콜백에 관한 Alyssa (Nick) Coghlan의 최초 게시글 [2]. 에 언급되어 있습니다. 이는 Python 3.0과 그 ABC를 개발하는 과정에서 발견되었습니다. 우리는 decimal.Decimal 같은 클래스를 ABC에 등록하고 싶었지만, 인터프리터를 시작할 때마다 해당 모듈을 임포트해서는 안 되었습니다. Alyssa는 다음 예제를 제시했습니다.:
@imp.when_imported('decimal')
def register(decimal):
Inexact.register(decimal.Decimal)
register 함수는 ‘decimal’이라는 이름의 모듈에 대한 콜백으로 등록됩니다. decimal이 임포트되면 모듈 객체를 인자로 하여 이 함수가 호출됩니다.
이 특정 예제는 실제로는 필요하지 않지만(2.6과 3.0에서 decimal.Decimal은 적절한 추상 Number 베이스 클래스를 상속할 것이므로), 그래도 원리를 잘 보여 줍니다.
기존 구현
PJE의 peak.util.imports [3]는 로드 후 훅을 구현합니다. 제 구현은 그의 구현과 많은 부분을 공유하며, 부분적으로 그의 아이디어에 기반합니다.
사후 임포트 훅 구현
사후 임포트 훅은 모듈이 로드된 후에 호출됩니다. 훅은 모듈 인스턴스라는 하나의 인자를 받는 호출 가능 객체입니다. 훅은 모듈의 점으로 구분된 이름(예: ‘os’ 또는 ‘os.path’)으로 등록됩니다.
호출 가능 객체는 딕셔너리 sys.post_import_hooks에 저장되며, 이 딕셔너리는 이름(문자열)을 호출 가능 객체의 리스트 또는 None에 대응시키는 매핑입니다.
상태
등록된 훅이 없음
sys.post_import_hooks에 해당 모듈에 대한 항목이 없습니다.
훅이 등록되었으며 모듈은 아직 로드되지 않음
임포트 훅 레지스트리에 sys.post_import_hooks[“name”] = [hook1] 항목이 있습니다.
모듈이 성공적으로 로드된 경우
임포트 메커니즘은 새로 로드된 모듈에 대한 사후 임포트 훅이 sys.post_import_hooks에 있는지 확인합니다. 훅이 발견되면 모듈 인스턴스를 첫 번째 인자로 하여 훅이 등록된 순서대로 호출합니다. 메서드가 예외를 발생시키면 훅 처리가 중단됩니다. 마지막으로, 오류가 발생한 경우에도 해당 모듈 이름에 대한 항목은 None으로 설정됩니다.
또한 훅 내부에서 알림 메서드가 호출될 때 무한 재귀를 방지하기 위해 모듈 객체의 새로운 __notified__ 슬롯을 True로 설정합니다. PyModule의 서브클래스가 아닌 객체의 경우에는 대신 새 속성이 추가됩니다.
모듈을 로드할 수 없는 경우
임포트 훅은 호출되지도 않고 레지스트리에서 제거되지도 않습니다. 나중에 모듈을 로드할 수 있을 수도 있습니다.
훅이 등록되었지만 모듈이 이미 로드된 경우
훅이 즉시 실행됩니다.
불변 조건
임포트 훅 시스템은 특정 불변 조건을 보장합니다. XXX
Python 구현 예시
Python 구현은 다음과 같을 수 있습니다.:
def notify(name):
try:
module = sys.modules[name]
except KeyError:
raise ImportError("Module %s has not been imported" % (name,))
if module.__notified__:
return
try:
module.__notified__ = True
if '.' in name:
notify(name[:name.rfind('.')])
for callback in post_import_hooks[name]:
callback(module)
finally:
post_import_hooks[name] = None
XXX
C API
새로운 C API 함수
PyObject* PyImport_GetPostImportHooks(void)- sys.post_import_hooks 딕셔너리 또는 NULL을 반환합니다.
PyObject* PyImport_NotifyLoadedByModule(PyObject *module)- 모듈이 요청되었음을 사후 임포트 시스템에 알립니다. 같은 모듈 객체에 대한 빌린 참조를 반환하며, 오류가 발생한 경우에는 NULL을 반환합니다. 이 함수 호출은 부모 모듈이 아닌 해당 모듈 자체의 훅만 호출합니다. 이 함수는 임포트 잠금을 획득한 상태에서 호출해야 합니다.
PyObject* PyImport_NotifyLoadedByName(const char *name)PyImport_NotifyLoadedByName("a.b.c")는a,a.b,a.b.c에 대해 바로 그 순서대로PyImport_NotifyLoadedByModule()을 호출합니다. 모듈은sys.modules에서 가져옵니다. 모듈을 가져올 수 없으면 예외가 발생하고, 그렇지 않으면modname에 대한 빌린 참조를 반환합니다. 훅 호출은 항상 최상위 부모 모듈부터 시작합니다. PyImport_NotifyLoadedByName()의 호출자는 반드시 임포트 잠금을 보유하고 있어야 합니다!PyObject* PyImport_RegisterPostImportHook(PyObject *callable, PyObject *mod_name)- 모듈
mod_name에 대한 새 훅callable을 등록합니다. int PyModule_GetNotified(PyObject *module)__notified__슬롯 / 속성의 상태를 반환합니다.int PyModule_SetNotified(PyObject *module, int status)__notified__슬롯 / 속성의 상태를 설정합니다.
PyImport_NotifyLoadedByModule() 메서드는 import_submodule() 내부에서 호출됩니다. 임포트 시스템은 임포트 잠금이 획득되었고 부모 모듈의 훅이 이미 호출되었음을 보장합니다.
Python API
임포트 훅 레지스트리와 두 개의 새 API 메서드는 sys 및 imp 모듈을 통해 노출됩니다.
sys.post_import_hooks- 이 dict에는 사후 임포트 훅이 담깁니다:
{"name" : [hook1, hook2], ...}
imp.register_post_import_hook(hook: "callable", name: str)- 모듈 name에 대한 새 훅 hook을 등록합니다.
imp.notify_module_loaded(module: "module instance") -> module- 모듈이 로드되었음을 시스템에 알립니다. 이 메서드는 기존의 지연(lazy / deferred) 임포트 확장과의 호환성을 위해 제공됩니다.
module.__notified__- 모듈 인스턴스의 슬롯입니다. XXX
when_imported 함수 데코레이터도 imp 모듈에 있으며, 다음과 동등합니다.:
def when_imported(name):
def register(hook):
register_post_import_hook(hook, name)
return register
- imp.when_imported(name) -> 데코레이터 함수
- @when_imported(name) def hook(module): pass 형태의 모듈 훅을 위한 것입니다
미해결 문제
when_imported 데코레이터는 아직 작성되지 않았습니다.
코드에는 XXX 주석이 여러 개 있습니다. 이들은 대부분 경계 사례에서의 오류 처리에 관한 것입니다.
하위 호환성
새로운 기능과 API는 Python의 기존 임포트 시스템과 충돌하지 않으며, 대부분의 소프트웨어에 하위 호환성 문제를 일으키지 않습니다. 하지만 자체적인 지연 임포트 기법을 구현하는 PEAK나 Zope 같은 시스템은 몇 가지 규칙을 따라야 합니다.
사후 임포트 훅은 기존의 유예 임포트 시스템 및 지연 임포트 시스템과 협력하도록 신중하게 설계되었습니다. PEP 작성자는 자체 on-load-hook을 새 훅 API로 대체할 것을 제안합니다. 대안적인 게으른(lazy) 임포트나 지연(deferred) 임포트도 계속 동작하지만, 그 구현은 imp.notify_module_loaded 함수를 호출해야 합니다.
참조 구현
참조 구현은 이미 작성되어 있으며 py3k-importhook 브랜치에서 사용할 수 있습니다. [4] 아직 약간의 정리 작업, 문서 갱신, 추가 단위 테스트가 필요합니다.
감사의 글
Alyssa Coghlan, 교정과 초기 논의에 감사드립니다. Phillip J. Eby, PEAK에서의 구현과 제 구현에 대한 도움에 감사드립니다.
Copyright
This document has been placed in the public domain.