2016-04-19 33 views
10

this SO question'a benzer bir yaklaşımla import hooks aracılığıyla sys.meta_path aracılığıyla kurulum yapmaya çalışıyorum. Bunun için, yukarıdaki bağlantıda açıklandığı gibi iki işlevi find_module ve load_module tanımlamalıyım. İşte benim load_module işlevi, çoğu modülleri için çalışıyor, ancak PyQt4.QtCore başarısızPyQt4.QtCore için ithalat kancaları

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 

olan Python 2.7 kullanırken:

döndürür
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 

aynı kod çalışır Python 3.4 ile (imp kullanımdan kaldırılmış olsa da ve importlib bunun yerine ideal olarak kullanılmalıdır).

Bunun SIP dinamik modül başlatması ile bir ilgisi olduğunu varsayalım. Python 2.7 ile denemem gereken başka bir şey var mı?

Not: Bu, hem PyQt4 hem de PyQt5 ile geçerlidir.

Düzenleme Bu

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

aynı hata başarısız olarak gerçekten this question ile ilişkili olabilir. Somut bir kullanım durumu, örneğin @Nikita 'ın talebi üzerine, ne yapmam çalışıyorum ithalat yöneltmek olan: Hala

Edit2 ... Etrafında yol olacağını emin değilim Yani import A, ne zaman import B olur. Bunun gerçekten de, bunun için find_spec/find_module numaralı modülün yeniden isimlendirilmesinin yeterli olacağını ve ardından load_module numaralı varsayılanı kullanacağını düşünebiliriz. Ancak, Python 2'de bir varsayılan load_module uygulamasının nerede olduğu belirsizdir. Benzer bir şey bulduğum en yakın uygulama future.standard_library.RenameImport'dur. importlib'un Python 3'ten 2'ye tümüyle uygulanmasının bir arka planı gibi görünmüyor.

Bu sorunu yeniden üreten içe aktarma kancaları için en az çalışan bir örnek, bu gist numaralı belgede bulunabilir.

+0

, yapmaya çalıştığım şey, [SiQt] (https://github.com/rth/SiQt) paketini bakınız ilişkin bazı genel bağlam kazandırmak ve bu sorunun etmek [bu github sorunu] 'nda tartışılmıştır (https://github.com/rth/SiQt/issues/4). – rth

+0

Gerçekten sorununuzu anlamıyorum ama '__import __ ('PyQt4.QtCore')' ile ilgili sorun nedir? sonsuz tekrarlamaya yol açıyor mu? – danidee

+0

@danidee __import __ ('A') 'ile yanlış bir şey yok, ama' import A' kullanmanın karşılığıdır. İstediğim şey, bunu yaptığınızda ne olacağını değiştirmek ve özellikle, A'yı içe aktarırken, "İçe aktarma B" yi çalıştırmaktır. Bu sys.meta_path' içinde içe aktarma kancaları ile yapılabilir, ancak bunlar "imp.load_module" gibi daha düşük seviyeli işlevler gerektirir. – rth

cevap

4

UPD: Bu bölüm, yanıt güncellemelerinden sonra gerçekten alakalı değil, aşağıdan yukarıya bakın.

Neden sadece Python 2.7 ve Python 3 kullanılabilir hem importlib.import_module kullanın:

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

dinamik bir modül olduğundan, dediği gibi: Ubuntu 14.04 üzerinde

#test.py 

import importlib 

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

hata (ve gerçek dosya QtCore.so), ayrıca imp.load_dynamic'a da bakabilir.

Modül başlatma kodunun yürütülmesini zorlamak için başka bir çözüm de olabilir, ancak IMO çok zor bir durumdur, bu nedenle neden sadece importlib kullanın.

UPD: pkgutil'da işler olabilir, bu yardımcı olabilir. Ne benim yorumunda bahsettiği, bu gibi bulucu değiştirmeye çalışırsanız:

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 

böylece kendiniz deneyin, şimdi yukarıdaki kodu test edebilir.

P.S. Python 3'te sizin için çalışan imp.load_module()'un deprecated since Python 3.3 olduğunu unutmayın.

Başka bir çözüm hiç kancalar kullanın, ancak bunun yerine sarmak için değil __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__ 

çıkışı:

<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 

shelvepickle olarak değiştirildi ve pickle varsayılan makine tarafından ithal edilmektedir değişken adı shelve.

+0

İlk iki fikrinize katılıyorum, maalesef çalışmıyorlar, daha önce denedim. a) Anladığım kadarıyla, 'importlib.import_module', 'sys.meta_path' içe aktarma kancalarını koymak için çok yüksek seviyededir. Ne olursa olsun bir paket içe aktardığınızda sys.meta_path' içinde görünecek ve 'load_module' işlevi' importlib.import_module' kullanıyorsa, 'sys.meta_path' içinde yine aynı' load_module'yi bulacaktır. fonksiyonu vb, böylece sonsuz bir özyineleme sorunu olsun ... İhtiyaç duyulan şey, 'imp.find_module' veya' importlib.machinery.SourceFileLoader – rth

+0

b gibi alt kaldıraçlı bir şeydir.load_dynamic', aynı sonucu üretir (çünkü "imp.load_module" ile adlandırılmalıdır). c) Evet, o modülü el ile başlatmamaya karar verdim. Anlamadığım şey, neden (yani, importlib.import_module'nın ne tür bir işlem yapması ve 'imp.load_module'nın yapamayacağı, bunu gerekli kılan). Aynı durum tüm PyQt4/PyQt4 alt modülleri için de geçerlidir. Ben elde etmeye çalışıyorum ne PyQt4.QtCore' ithal edildiğinde 'SiQt.QtCore' ithalat. Python future.standard_library.RenameImport bunu PY2'de yapabildiğinden (aslında sadece ithalatı yeniden adlandırıyor) bu mümkün. – rth

+1

@rth, içe aktarma kancaları hakkında sağladığınız bağlantı ile, yolun her parçası için meta yol bulucusunun 'find_spec'/'find_module 'öğesini yinelemeli olarak çağıracağını söylüyor. Örneğin. mpf.find_spec ("PyQt4", Yok, Yok) 've sonra bir tane daha" mpf.find_spec ("PyQt4.QtCore", PyQt4 .__ path__, None) '. Eğer 'find_spec' ya da mpf'nin başka bir parçasının yerine takılırsanız, 'String' ile 'PyQt4' yerine isim dizisinde 'SiQt' konabilir ve daha sonra 'SiQt' yi kendi kendine yüklemek için varsayılan makineyi çağırabilir. Eğer yanılıyorsam, lütfen neyi başarmaya çalıştığınızı daha iyi anlamak için kancalar için kullanılan bazı kodları sağlayın. – Nikita

3

PyQt4.QtCore gibi bir paketin parçası olan bir modülü bulduğunuzda, adın her bölümünü . olmadan yinelemeli olarak bulmanız gerekir. Ve imp.load_module, . ayırma paketi ve modül adı ile name parametresinin tam modül adı olmasını gerektirir.

QtCore bir paketin bir parçası olduğundan, bunun yerine python -c 'import PyQt4.QtCore' yapmalısınız. İşte bir modül yüklemek için kod.

Test; o yararlı olabilir Eğer

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