2017-04-23 9 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のinstead.'はあなたの問題を解決していないことをしていますか? –

+0

私はimp.modulesをこのように置き換えなければならないことを知りましたが、ドキュメンテーションはimportlibのmodule_from_specを使用することを示しています。私はimpからカスタムフックインポータを実行する3つのメソッドを使用しており、importlibに相当するものを探す必要があります。 –

答えて

5

find_moduleおよびload_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() 
関連する問題