Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 547 – -m 옵션을 사용하여 확장 모듈 실행

Author:
Marcel Plch <gmarcel.plch at gmail.com>, Petr Viktorin <encukou at gmail.com>
Status:
Deferred
Type:
Standards Track
Created:
25-May-2017
Python-Version:
3.7
Post-History:


Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

연기 공지

이 PEP의 가장 중요한 사용 사례이자 유일하게 명시된 사용 사례인 Cython은 아직 다중 단계 초기화를 지원할 준비가 되지 않았습니다. Cython은 C 수준의 정적 변수에 전역 상태를 유지합니다. Cython issue 1923의 논의를 참조하십시오.

상황이 바뀔 때까지 이 PEP는 연기됩니다.

초록

이 PEP는 PEP 489 다중 단계 초기화를 사용하여 내장 모듈과 확장 모듈을 __main__ 네임스페이스에서 실행할 수 있도록 하는 구현을 제안합니다.

이를 통해 다중 단계 초기화가 활성화된 모듈을 다음 명령으로 실행할 수 있습니다.:

$ python3 -m _testmultiphase
This is a test module named __main__.

동기

현재 확장 모듈은 Python 소스 모듈의 모든 기능을 지원하지 않습니다. 구체적으로는 Python의 -m 옵션을 사용하여 확장 모듈을 스크립트로 실행할 수 없습니다.

이를 가능하게 하기 위한 기술적 기반은 PEP 489에서 이미 마련되었으며, -m 옵션 활성화는 해당 PEP의 “Possible Future Extensions” 절에 나열되어 있습니다. 기술적으로 여기에서 제안하는 추가 변경 사항은 비교적 작습니다.

근거

확장 모듈이 -m 옵션을 지원하지 않는 문제는 전통적으로 Python 래퍼를 제공하여 우회해 왔습니다. 예를 들어, _pickle 모듈의 명령줄 인터페이스는 순수 Python 재구현과 함께 순수 Python pickle 모듈에 들어 있습니다.

C API를 사용하여 명령줄 인터페이스를 구축하는 일은 번거롭기 때문에 표준 라이브러리 모듈에서는 이러한 방식이 잘 작동합니다. 그러나 다른 사용자는 실행 가능한 확장 모듈을 직접 만들고자 할 수 있습니다.

중요한 사용 사례로 C 확장 모듈로 컴파일되는 Python 유사 언어인 Cython이 있습니다. Cython은 Python의 (거의) 상위 집합입니다. 즉, Python 모듈을 Cython으로 컴파일해도 일반적으로 모듈의 기능이 변경되지 않으므로 Cython 전용 기능을 점진적으로 추가할 수 있습니다. 이 PEP를 통해 Cython 확장 모듈은 -m 옵션으로 실행할 때 Python 대응 모듈과 동일하게 동작할 수 있습니다. Cython 개발자들은 이 기능을 구현할 가치가 있다고 판단합니다(Cython issue 1715 참조).

배경

Python의 -m 옵션은 runpy._run_module_as_main 함수에서 처리합니다.

-m으로 지정된 모듈은 일반적으로 임포트되지 않습니다. 대신 인터프리터 초기화 과정에서 상당히 일찍 생성되는 __main__ 모듈의 네임스페이스에서 실행됩니다.

Python 소스 모듈의 경우 다른 모듈의 네임스페이스에서 실행해도 문제가 없습니다. 코드가 기존 모듈의 __dict__를 대상으로 localsglobals를 설정한 상태에서 실행되기 때문입니다. 확장 모듈의 경우에는 그렇지 않습니다. 확장 모듈의 PyInit_* 진입점은 전통적으로 PyModule_Create를 사용하여 새 모듈 객체를 생성하는 동시에 이를 초기화했기 때문입니다.

Python 3.5부터 확장 모듈은 PEP 489 다단계 초기화를 사용할 수 있습니다. 이 시나리오에서 PyInit_* 진입점은 PyModuleDef 구조체를 반환합니다. 이 구조체는 모듈을 생성하고 초기화하는 방법을 설명합니다. 확장 모듈은 Py_mod_create 콜백을 사용하여 모듈 객체의 생성을 사용자 정의하거나, Py_mod_create를 지정하지 않고 일반 모듈 객체를 사용할 수 있습니다. 그런 다음 다른 콜백인 Py_mod_exec가 모듈 객체를 초기화하기 위해 호출됩니다. 예를 들어 메서드와 클래스를 채우는 방식입니다.

제안

다단계 초기화를 사용하면 확장 모듈을 다른 모듈의 네임스페이스에서 실행할 수 있습니다. Py_mod_create 콜백을 지정하지 않은 경우 __main__ 모듈을 Py_mod_exec 콜백에 전달하여 초기화할 수 있으며, 이는 __main__이 새로 생성된 모듈 객체인 것처럼 동작합니다.

이 방식에서 한 가지 복잡한 점은 C 수준의 모듈 상태입니다. 각 모듈에는 확장 모듈이 생성될 때 할당된 메모리 영역을 가리키는 md_state 포인터가 있습니다. PyModuleDef는 할당할 메모리의 크기를 지정합니다.

구현에서는 md_state 메모리가 최대 한 번만 할당되도록 주의해야 합니다. 또한 Py_mod_exec 콜백은 모듈마다 한 번만 호출되어야 합니다. 여러 번 초기화된 모듈의 영향은 너무 미묘하므로 확장 모듈 작성자가 이를 추론하도록 요구해서는 안 됩니다. md_state 포인터 자체가 보호 장치 역할을 합니다. 메모리를 할당하고 Py_mod_exec를 호출하는 작업은 항상 함께 수행되며, md_state가 이미 non-NULL이면 확장 모듈 초기화에 실패합니다.

__main__ 모듈은 확장 모듈로 생성되지 않으므로 해당 md_state는 일반적으로 NULL입니다. __main__의 컨텍스트에서 확장 모듈을 초기화하기 전에 해당 모듈의 PyModuleDef에 따라 모듈 상태가 할당됩니다.

이러한 변경을 전반적으로 가능하게 하기 위해 PEP 489가 설계되었지만, 확장 모듈의 모듈 검색, 생성 및 초기화 단계를 분리하여 새로 초기화된 모듈 대신 다른 모듈을 사용할 수 있도록 해야 하며, 이 기능을 runpyimportlib에 추가해야 합니다.

사양

importlib 로더에 대한 새로운 선택적 메서드가 추가됩니다. 이 메서드는 exec_in_module이라고 하며, 두 개의 위치 인자, 즉 모듈 사양과 이미 존재하는 모듈을 받습니다. __spec__ 또는 __name__과 같은 가져오기 관련 속성이 모듈에 이미 설정되어 있더라도 무시됩니다.

runpy._run_module_as_main 함수는 이 새로운 로더 메서드를 찾습니다. 이 메서드가 있으면 runpy는 모듈의 Python 코드를 로드하고 실행하려고 하는 대신 이 메서드를 실행합니다. 그렇지 않으면 runpy는 이전과 같이 동작합니다.

ExtensionFileLoader 변경 사항

importlib의 ExtensionFileLoader에는 _imp.exec_in_module이라는 새로운 함수를 호출하는 exec_in_module 구현이 추가됩니다.

_imp.exec_in_module은 기존 메커니즘을 사용하여 확장 모듈의 PyInit_* 함수를 찾고 호출합니다.

PyInit_* 함수는 완전히 초기화된 모듈(단일 단계 초기화) 또는 PEP 489 다단계 초기화를 위한 PyModuleDef를 반환할 수 있습니다.

단일 단계 초기화의 경우 _imp.exec_in_moduleImportError를 발생시킵니다.

다단계 초기화의 경우 PyModuleDef와 초기화할 모듈이 새로운 함수인 PyModule_ExecInModule에 전달됩니다.

이 함수는 PyModuleDefPy_mod_create 슬롯을 지정하거나 모듈이 이미 초기화된 경우(즉, 해당 md_state 포인터가 NULL이 아닌 경우) ImportError를 발생시킵니다. 그렇지 않으면 이 함수는 PyModuleDef에 따라 모듈을 초기화합니다.

하위 호환성

이 PEP는 하위 호환성을 유지합니다. 이 PEP는 새로운 함수들만 추가하며, 이전에는 __main__으로 모듈을 실행하는 것을 지원하지 않았던 로더를 위해 새로운 로더 메서드를 추가할 뿐입니다.

참조 구현

이 PEP의 참조 구현은 GitHub에서 확인할 수 있습니다.

참고 문헌