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 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
철회 공지
Python 3.3에서 importlib로 마이그레이션된 이후 상세 설계의 상당 부분이 더 이상 유효하지 않으므로, 이 PEP는 작성자에 의해 철회되었습니다.
초록
이 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이 임포트되면 이 함수가 모듈 객체를 인자로 받아 호출됩니다.
이 특정 예는 실제로는 필요하지 않지만 (decimal.Decimal이 2.6과 3.0에서 적절한 추상 Number 베이스 클래스에서 상속받게 되므로), 여전히 원리를 보여 줍니다.
기존 구현
PJE의 peak.util.imports [3]는 로드 후크를 구현합니다. 제 구현은 그의 구현과 많은 부분을 공유하며, 일부는 그의 아이디어에 기반합니다.
임포트 후크 구현
임포트 후크는 모듈이 로드된 후 호출됩니다. 후크는 하나의 인자, 즉 모듈 인스턴스를 받는 호출 가능 객체입니다. 후크는 모듈의 점으로 구분된 이름(예: ‘os’ 또는 ‘os.path’)으로 등록됩니다.
호출 가능 객체는 sys.post_import_hooks딕셔너리에 저장되며, 이는 이름(문자열)을 호출 가능 객체의 목록 또는 None에 매핑합니다.
상태
등록된 후크가 없음
sys.post_import_hooks에는 해당 모듈에 대한 항목이 없습니다.
후크가 등록되었지만 모듈은 아직 로드되지 않음
임포트 후크 레지스트리에 다음 항목이 포함됩니다. sys.post_import_hooks[“name”] = [hook1]
모듈이 성공적으로 로드됩니다.
임포트 메커니즘은 새로 로드된 모듈에 대한 post import 훅이 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)- 모듈이 요청되었음을 post import 시스템에 알립니다. 동일한 모듈 객체에 대한 빌린 참조를 반환하거나 오류가 발생한 경우 NULL을 반환합니다. 이 함수는 모듈 자체에 대한 훅만 호출하고 부모 모듈에 대한 훅은 호출하지 않습니다. 이 함수는 임포트 잠금을 획득한 상태에서 호출해야 합니다.
PyObject* PyImport_NotifyLoadedByName(const char *name)PyImport_NotifyLoadedByName("a.b.c")는 특정 순서로PyImport_NotifyLoadedByModule()을a,a.b및a.b.c에 대해 호출합니다. 모듈은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() 내부에서 호출됩니다. 임포트 시스템은 임포트 잠금이 획득되고 부모 모듈들에 대한 훅이 이미 호출되었는지를 확인합니다.
파이썬 API
임포트 훅 레지스트리와 두 개의 새로운 API 메서드는 sys 모듈과 imp 모듈을 통해 노출됩니다.
sys.post_import_hooks- 이 딕셔너리는 임포트 후 훅들을 포함합니다.:
{"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) -> 데코레이터함수
- for @when_imported(name) def hook(module): pass
미해결 문제
when_imported 데코레이터는 아직 작성되지 않았습니다.
코드에는 여러 개의 XXX 주석이 포함되어 있습니다. 대부분 예외적인 경우의 오류 처리에 관한 것입니다.
하위 호환성
새로운 기능과 API는 파이썬의 기존 임포트 시스템과 충돌하지 않으며, 대부분의 소프트웨어에 대해 어떠한 하위 호환성 문제도 일으키지 않습니다. 그러나 자체적인 지연 임포트 매직을 구현하는 PEAK, Zope 같은 시스템은 몇 가지 규칙을 따라야 합니다.
사후 임포트 훅은 기존의 지연 임포트 시스템 및 레이지 임포트 시스템과 협력하도록 신중하게 설계되었습니다. 이는 자체 on-load-hook을 새 훅 API로 대체하라는 PEP 저자의 제안입니다. 대안적인 레이지 임포트나 지연 임포트도 계속 작동하지만, 구현체는 imp.notify_module_loaded 함수를 호출해야 합니다.
참조 구현
참조 구현은 이미 작성되어 있으며 py3k-importhook 브랜치에서 확인할 수 있습니다. [4] 아직 약간의 정리, 문서 갱신, 추가 단위 테스트가 필요합니다.
감사의 말
교정과 초기 논의를 맡아준 Alyssa Coghlan, Phillip J. Eby, PEAK에서의 그의 구현과 제 구현에 대한 도움에 감사드립니다.
Copyright
This document has been placed in the public domain.