2017-04-23 11 views
2

더 이상 사용되지 않는 모듈 imp을 사용하여 Python으로 가져 오기/실행하기 전에 모듈의 소스 코드를 즉시 수정하는 사용자 정의 가져 오기 훅을 작성할 수 있습니다. 다음 아래 source라는 문자열, 모듈을 만드는 데 필요한 필수 코드와 소스 코드를 감안할 때 : imp 이후importlib을 사용하여 소스 코드를 즉시 수정할 수있는 가져 오기 훅을 구현하는 방법은 무엇입니까?

module = imp.new_module(name) 
sys.modules[name] = module 
exec(source, module.__dict__) 

이되지 않습니다, 나는 importlib와 비슷한 일을하고 싶습니다. [편집 : 사용자 정의 가져 오기 후크를 빌드하려면 다른 imp 메서드를 대체해야합니다 - 그래서 내가 찾고있는 대답은 단순히 위의 코드를 바꾸는 것이 아닙니다.]

그러나 그림을 그릴 수 없었습니다. 이것을하는 방법을 밖으로. importlib documentation에는 function to create modules from "specs"이 있는데, 내가 알 수있는 한, 문자열에서 모듈을 만들 수 있도록 명확한 방법이없는 자체 로더를 포함하는 객체입니다.

이것을 증명하기 위해 minimal example을 만들었습니다. 자세한 내용은 readme 파일을 참조하십시오.

+0

'imp.new_module' 문서를 보면, 3.4 버전부터는 더 이상 사용되지 않을 것입니다 : 대신 types.ModuleType을 사용하십시오. 문제가 해결되지 않습니까? –

+0

imp.modules를이 방법으로 교체해야한다는 것을 알았지 만 문서에서 modulelfromspec (importlib의)을 사용하도록 명시하고 있습니다. imp에서 3 가지 방법을 사용하여 사용자 지정 후크 가져 오기를 수행하고 importlib에 해당하는 것을 찾아야합니다. –

답변

5

find_moduleload_module은 모두 사용되지 않습니다. find_spec 및 (create_moduleexec_module) 모듈로 각각 전환해야합니다. 자세한 내용은 importlibdocumentation을 참조하십시오.

또한 MetaPathFinder 또는 PathEntryFinder을 호출 할 시스템이 다른지 여부를 검사해야합니다. 즉, 메타 경로 파인더가 먼저 실행되고 내장 모듈을 오버라이드 할 수 있지만 경로 입력 찾기는 sys.path에있는 모듈에 대해 특별히 작동합니다.

다음은 전체 수입 기계를 교체하려는 매우 기본적인 수입업자입니다. 기능 (find_spec, create_moduleexec_module)을 사용하는 방법을 보여줍니다.

import sys 
import os.path 

from importlib.abc import Loader, MetaPathFinder 
from importlib.util import spec_from_file_location 

class MyMetaFinder(MetaPathFinder): 
    def find_spec(self, fullname, path, target=None): 
     if path is None or path == "": 
      path = [os.getcwd()] # top level import -- 
     if "." in fullname: 
      *parents, name = fullname.split(".") 
     else: 
      name = fullname 
     for entry in path: 
      if os.path.isdir(os.path.join(entry, name)): 
       # this module has child modules 
       filename = os.path.join(entry, name, "__init__.py") 
       submodule_locations = [os.path.join(entry, name)] 
      else: 
       filename = os.path.join(entry, name + ".py") 
       submodule_locations = None 
      if not os.path.exists(filename): 
       continue 

      return spec_from_file_location(fullname, filename, loader=MyLoader(filename), 
       submodule_search_locations=submodule_locations) 

     return None # we don't know how to import this 

class MyLoader(Loader): 
    def __init__(self, filename): 
     self.filename = filename 

    def create_module(self, spec): 
     return None # use default module creation semantics 

    def exec_module(self, module): 
     with open(self.filename) as f: 
      data = f.read() 

     # manipulate data some way... 

     exec(data, vars(module)) 

def install(): 
    """Inserts the finder into the import machinery""" 
    sys.meta_path.insert(0, MyMetaFinder()) 

다음은 더 많은 수입 기계를 재사용하려고하는 약간 더 민감한 버전입니다. 따라서 모듈 원본을 가져 오는 방법 만 정의하면됩니다.

import sys 
from os.path import isdir 
from importlib import invalidate_caches 
from importlib.abc import SourceLoader 
from importlib.machinery import FileFinder 


class MyLoader(SourceLoader): 
    def __init__(self, fullname, path): 
     self.fullname = fullname 
     self.path = path 

    def get_filename(self, fullname): 
     return self.path 

    def get_data(self, filename): 
     """exec_module is already defined for us, we just have to provide a way 
     of getting the source code of the module""" 
     with open(filename) as f: 
      data = f.read() 
     # do something with data ... 
     # eg. ignore it... return "print('hello world')" 
     return data 


loader_details = MyLoader, [".py"] 

def install(): 
    # insert the path hook ahead of other path hooks 
    sys.path_hooks.insert(0, FileFinder.path_hook(loader_details)) 
    # clear any loaders that might already be in use by the FileFinder 
    sys.path_importer_cache.clear() 
    invalidate_caches()