2016-04-19 10 views
10

と多少似たようなアプローチで、import hooksからsys.meta_pathまで設定しようとしています。このためには、上のリンクで説明したように、2つの関数find_moduleload_moduleを定義する必要があります。PyQt4.QtCoreのインポートフック

name = "QtCore" 
path = ['/usr/lib64/python2.7/site-packages/PyQt4'] 

mod = load_module(name, path) 

返し、

Traceback (most recent call last): 
    File "test.py", line 19, in <module> 
    mod = load_module(name, path) 
    File "test.py", line 13, in load_module 
    module = imp.load_module(name, fp, pathname, description) 
SystemError: dynamic module not initialized properly 

同じコードの作品:ここではほとんどのモジュールのため正常に動作しますが、Pythonの2.7を使用している場合PyQt4.QtCoreのために失敗した私のload_module機能、

import imp 

def load_module(name, path): 
    fp, pathname, description = imp.find_module(name, path) 

    try: 
     module = imp.load_module(name, fp, pathname, description) 
    finally: 
     if fp: 
      fp.close() 
    return module 

ですPython 3.4では問題ありませんが(impは廃止予定ですが、代わりにimportlibが理想的です)。

これは、SIPダイナミックモジュールの初期化と関係があると思います。 Python 2.7を試してみるべきことはありますか?

注:これはPyQt4PyQt5の両方に適用されます。

編集は:これは確かとしてthis questionに関連している可能性があり、

cd /usr/lib64/python2.7/site-packages/PyQt4 
python2 -c 'import QtCore' 

は同じエラーで失敗します。それでも私は...

EDIT2それを回避する方法がどうなるかわからない。具体的な使用例について@Nikitaの要求に従って、私は何をしようとしていることは、インポートをリダイレクトすることですですので、import Aを実行すると、何が起こるのでしょうかimport Bです。これについては、find_spec/find_moduleでモジュールの名前を変更してから、デフォルトのload_moduleを使用すれば十分でしょう。しかし、Python 2でデフォルトのload_module実装がどこにあるのかは不明です。私が見つけた最も近い実装はfuture.standard_library.RenameImportです。 2.

へのPython 3から importlibの完全な実装のバックポート

再現し、この問題は、このgistで見つけることができますインポートフックのための最小限の作業例があるようにそれは見えません。

+0

それは、有用であるかもしれない私が何をしようとしているため、いくつかの一般的な文脈を与えるために、[SiQt](https://github.com/rth/SiQt)パッケージを見て、この問題がある場合[このgithub問題](https://github.com/rth/SiQt/issues/4)で議論されています。 – rth

+0

私はあなたの問題を本当に理解できませんが、 '__import __( 'PyQt4.QtCore')'の何が間違っていますか。それは無限再帰につながるのだろうか? – danidee

+0

@danidee '__import __( 'A')'には何も問題はありませんが、 'import A'を使うのと同じです。私が望むのは、あなたがそれをするときに起こることを変え、特に 'import A 'を実行するときに' import B'を実行することです。これは 'sys.meta_path'のインポートフックで行うことができますが、' imp.load_module'のようなより低いレベルの関数を必要とします。 – rth

答えて

4

UPD:この部分は、回答の更新後はあまり関係ありませんので、下記のUPDをご覧ください。

理由だけのPython 2.7とPython 3の両方で利用可能である、importlib.import_moduleを使用しない:Ubuntuの14.04で

#test.py 

import importlib 

mod = importlib.import_module('PyQt4.QtCore') 
print(mod.__file__) 

を:

$ python2 test.py 
/usr/lib/python2.7/dist-packages/PyQt4/QtCore.so 

に述べたように、それは、動的モジュールのためエラー(と実際のファイルはQtCore.soです)、またimp.load_dynamicをご覧ください。

もう1つの解決策は、モジュール初期化コードの実行を強制することですが、IMOはあまりにも面倒です。なぜimportlibを使用しないのですか。

UPDpkgutilには、役立つ可能性があります。私は私のコメントで話していた、このようなあなたのファインダーを変更しよう:

import pkgutil 

class RenameImportFinder(object): 

    def find_module(self, fullname, path=None): 
     """ This is the finder function that renames all imports like 
      PyQt4.module or PySide.module into PyQt4.module """ 
     for backend_name in valid_backends: 
      if fullname.startswith(backend_name): 
       # just rename the import (That's what i thought about) 
       name_new = fullname.replace(backend_name, redirect_to_backend) 
       print('Renaming import:', fullname, '->', name_new,) 
       print(' Path:', path) 


       # (And here, don't create a custom loader, get one from the 
       # system, either by using 'pkgutil.get_loader' as suggested 
       # in PEP302, or instantiate 'pkgutil.ImpLoader'). 

       return pkgutil.get_loader(name_new) 

       #(Original return statement, probably 'pkgutil.ImpLoader' 
       #instantiation should be inside 'RenameImportLoader' after 
       #'find_module()' call.) 
       #return RenameImportLoader(name_orig=fullname, path=path, 
       #  name_new=name_new) 

    return None 

はそれを自分で試してみてください、今すぐ上記のコードをテストすることはできません。

P.S. Python 3で働いたimp.load_module()deprecated since Python 3.3です。

別の解決策が全くフックを使用することではなく、__import__をラップ:

print(__import__) 

valid_backends = ['shelve'] 
redirect_to_backend = 'pickle' 

# Using closure with parameters 
def import_wrapper(valid_backends, redirect_to_backend): 
    def wrapper(import_orig): 
     def import_mod(*args, **kwargs): 
      fullname = args[0] 
      for backend_name in valid_backends: 
       if fullname.startswith(backend_name): 
        fullname = fullname.replace(backend_name, redirect_to_backend) 
        args = (fullname,) + args[1:] 
      return import_orig(*args, **kwargs) 
     return import_mod 
    return wrapper 

# Here it's important to assign to __import__ in __builtin__ and not 
# local __import__, or it won't affect the import statement. 
import __builtin__ 
__builtin__.__import__ = import_wrapper(valid_backends, 
             redirect_to_backend)(__builtin__.__import__) 

print(__import__) 

import shutil 
import shelve 
import re 
import glob 

print shutil.__file__ 
print shelve.__file__ 
print re.__file__ 
print glob.__file__ 

出力:pickleに改名

<built-in function __import__> 
<function import_mod at 0x02BBCAF0> 
C:\Python27\lib\shutil.pyc 
C:\Python27\lib\pickle.pyc 
C:\Python27\lib\re.pyc 
C:\Python27\lib\glob.pyc 

shelve、およびpickleをして、デフォルトの機構によってインポートされます変数名shelve

+0

私はあなたの2つの最初のアイデアに同意します、残念ながら、彼らは動作しません、私は前にそれを試してみました。 a)理解しているところでは、 'importlib.import_module'は高すぎて' sys.meta_path'インポートフックに入れません。何が起こるかは 'sys.meta_path'で見るパッケージをインポートし、' load_module'関数が 'importlib.import_module'を使うと' sys.meta_path'を再び見て、同じ 'load_module'を見つけます。関数などがあるので、無限の再帰問題が発生します。必要なのは、 'imp.find_module'や' importlib.machinery.SourceFileLoader – rth

+0

のような低いレベルのものです。b) 'impを試しました。load_dynamic'を実行すると、同じ結果が生成されます(これは 'imp.load_module'によって呼び出されなければならないためです)。 c)はい、私はむしろモジュールを手作業で初期化しないことを知っています。私が理解できないのは、なぜ必要なのか(すなわち、 'importlib.import_module'という操作が何で、' imp.load_module'が必要でないのか)です。すべてのPyQt4/PyQt4サブモジュールで同じことが言えます。私が達成しようとしているのは 'PyQt4.QtCore'がインポートされた時に' SiQt.QtCore'をインポートすることです。これはPython future.standard_library.RenameImportがPY2(これは本質的には名前の変更による名前変更)で実行されるので、これは可能です。 – rth

+1

@rthのように、インポートフックに関するリンクによって、メタパスファインダはパスの各部分に対して 'find_spec' /' find_module'を再帰的に呼び出します。例えば。 '' mpf.find_spec( "PyQt4"、None、None) 'そしてさらにもう一つ' mpf.find_spec( "PyQt4.QtCore"、PyQt4 .__ path__、None) 'を実行します。だから、 'find_spec'やmpfの他の部分にフックしているのであれば、' PyQt4'を名前文字列の 'SiQt'に置き換え、' SiQt'をロードするためにデフォルトマシンを呼び出してください。私が間違っている場合、あなたが達成しようとしていることをよりよく理解するためにフックに使用されるいくつかのコードを提供してください。 – Nikita

3

PyQt4.QtCoreのようなパッケージの一部であるモジュールを見つけるときには、.を使わずに名前の各部分を再帰的に見つけなければなりません。 imp.load_moduleの場合、nameパラメータは完全なモジュール名で、.パッケージとモジュール名を分離する必要があります。

QtCoreはパッケージに含まれているため、代わりにpython -c 'import PyQt4.QtCore'を使用してください。モジュールをロードするコードは次のとおりです。

import imp 

def load_module(name): 
    def _load_module(name, pkg=None, path=None): 
     rest = None 
     if '.' in name: 
      name, rest = name.split('.', 1) 
     find = imp.find_module(name, path) 
     if pkg is not None: 
      name = '{}.{}'.format(pkg, name) 
     try: 
      mod = imp.load_module(name, *find) 
     finally: 
      if find[0]: 
       find[0].close() 
     if rest is None: 
      return mod 
     return _load_module(rest, name, mod.__path__) 
    return _load_module(name) 

テスト;

print(load_module('PyQt4.QtCore').qVersion()) 
4.8.6