2016-04-19 20 views
10

Tôi đang cố gắng thiết lập một số import hooks qua sys.meta_path, theo cách tương tự tương tự như this SO question. Đối với điều này, tôi cần phải xác định hai chức năng find_moduleload_module như được giải thích trong liên kết ở trên. Dưới đây là load_module chức năng của tôi,Móc nhập khẩu cho PyQt4.QtCore

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 

mà hoạt động tốt cho hầu hết các mô-đun, nhưng không cho PyQt4.QtCore khi sử dụng Python 2.7:

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

mod = load_module(name, path) 

trả về,

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 

Các tác phẩm cùng mã tốt với Python 3.4 (mặc dù imp không được dùng nữa và thay vào đó, importlib nên được sử dụng lý tưởng).

Tôi cho rằng điều này có liên quan đến việc khởi tạo mô-đun động SIP. Có điều gì khác tôi nên thử với Python 2.7?

Lưu ý: điều này áp dụng cả với PyQt4PyQt5.

Sửa: đây có thể liên quan đến this question như thật vậy,

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

thất bại với lỗi tương tự. Tuy nhiên tôi không chắc chắn những gì sẽ là một con đường xung quanh nó ...

Edit2: sau yêu cầu @Nikita 's cho một trường hợp sử dụng cụ thể ví dụ, những gì tôi đang cố gắng làm là để chuyển hướng nhập khẩu , vì vậy khi có import A, điều gì sẽ xảy ra là import B. Người ta thực sự có thể nghĩ rằng đối với điều này nó sẽ là đủ để làm đổi tên mô-đun trong find_spec/find_module và sau đó sử dụng mặc định load_module. Tuy nhiên, không rõ nơi để tìm một thực hiện mặc định load_module trong Python 2. Việc thực hiện gần nhất tôi đã tìm thấy một cái gì đó tương tự là future.standard_library.RenameImport. Có vẻ như không có cổng sau của việc triển khai hoàn chỉnh importlib từ Python 3 đến 2.

Ví dụ làm việc tối thiểu cho móc nhập tái tạo vấn đề này có thể được tìm thấy trong số gist này.

+0

Nếu nó có thể hữu ích, để cung cấp cho một số bối cảnh chung của những gì tôi đang cố gắng để làm, thấy gói [SiQt] (https://github.com/rth/SiQt), và vấn đề này là đã thảo luận trong [vấn đề github này] (https://github.com/rth/SiQt/issues/4). – rth

+0

tôi thực sự không hiểu vấn đề của bạn nhưng có gì sai với '__import __ ('PyQt4.QtCore')'. nó có dẫn tới đệ quy vô hạn? – danidee

+0

@danidee Không có gì sai với '__import __ ('A')', nhưng nó tương đương với việc sử dụng 'import A'. Những gì tôi muốn là thay đổi những gì xảy ra khi bạn làm điều đó, và đặc biệt chạy 'import B', khi bạn' import A'. Điều này có thể được thực hiện với các móc nhập khẩu trong 'sys.meta_path', nhưng chúng đòi hỏi các hàm mức thấp hơn như' imp.load_module'. – rth

Trả lời

4

UPD: Phần này không thực sự phù hợp sau khi cập nhật câu trả lời, vì vậy hãy xem UPD bên dưới.

Tại sao không chỉ cần sử dụng importlib.import_module, trong đó có sẵn trong cả hai Python 2.7 và Python 3:

#test.py 

import importlib 

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

trên Ubuntu 14.04:

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

Kể từ đó là một mô-đun năng động, như đã nói ở lỗi (và tệp thực tế là QtCore.so), cũng có thể xem imp.load_dynamic.

Một giải pháp khác có thể là buộc thực thi mã khởi tạo mô-đun, nhưng IMO quá phức tạp, vậy tại sao không chỉ sử dụng importlib.

UPD: Có những thứ trong pkgutil, điều đó có thể hữu ích. Những gì tôi đã nói trong nhận xét của tôi, hãy cố gắng sửa đổi công cụ tìm của bạn như sau:

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 

Không thể kiểm tra mã ở trên bây giờ, vì vậy hãy tự mình thử.

P.S. Lưu ý rằng imp.load_module(), mà làm việc cho bạn trong Python 3 là deprecated since Python 3.3.

Một giải pháp khác là không sử dụng lưỡi câu ở tất cả, mà thay vào đó quấn __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__ 

đầu ra:

<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 đổi tên thành pickle, và pickle được nhập khẩu bởi máy móc thiết bị mặc định với tên biến số shelve.

+0

Tôi đồng ý với hai ý tưởng đầu tiên của bạn, rất tiếc là chúng không hoạt động, tôi đã thử nó trước đây. a) Theo như tôi hiểu, 'importlib.import_module' là mức quá cao để đặt vào một móc nhập' sys.meta_path'. Điều gì xảy ra là khi bạn nhập một gói, nó sẽ tìm trong 'sys.meta_path', và nếu hàm' load_module' sử dụng 'importlib.import_module', nó sẽ tìm trong' sys.meta_path' một lần nữa, nó sẽ tìm cùng 'load_module' chức năng vv, vì vậy bạn nhận được một vấn đề đệ quy vô hạn ... Điều gì là cần thiết là một cái gì đó của đòn bẩy thấp hơn như 'imp.find_module' hoặc' importlib.machinery.SourceFileLoader – rth

+0

b) Tôi đã thử 'imp.load_dynamic', nó tạo ra cùng một kết quả (vì nó phải được gọi bởi 'imp.load_module' tôi giả sử). c) Có, tôi biết tôi không muốn khởi tạo mô-đun đó bằng tay. Những gì tôi không hiểu là lý do tại sao tôi phải (tức là hoạt động 'importlib.import_module' nào và' imp.load_module' không làm điều này cần thiết). Điều này cũng đúng đối với tất cả các submodules PyQt4/PyQt4. Những gì tôi đang cố gắng để đạt được là nhập 'SiQt.QtCore' khi' PyQt4.QtCore' được nhập. Tôi biết điều này là có thể kể từ python future.standard_library.RenameImport hiện nó trong PY2 (về cơ bản nó chỉ là đổi tên nhập khẩu). – rth

+1

@rth, bởi liên kết bạn cung cấp về móc nhập, nó nói rằng trình tìm đường dẫn meta sẽ gọi 'find_spec' /' find_module' đệ quy cho mỗi phần của đường dẫn. Ví dụ. 'mpf.find_spec (" PyQt4 ", None, None)' và sau đó thêm một 'mpf.find_spec (" PyQt4.QtCore ", PyQt4 .__ path__, None)'. Vì vậy, nếu bạn đang hooking thay cho 'find_spec' hoặc trong một số phần khác của mpf, có thể thay thế' PyQt4' bằng 'SiQt' trong chuỗi tên và sau đó gọi máy móc mặc định để cho phép nó tải' SiQt' một mình. Nếu tôi sai, xin vui lòng, cung cấp một số mã được sử dụng cho móc để hiểu rõ hơn những gì bạn đang cố gắng để thực hiện. – Nikita

3

Khi tìm một mô-đun là một phần của gói như PyQt4.QtCore, bạn phải tìm đệ quy mỗi phần của tên mà không cần .. Và imp.load_module yêu cầu thông số name là tên đầy đủ của mô-đun với . tách gói và tên mô-đun.

QtCore là một phần của gói, bạn nên làm python -c 'import PyQt4.QtCore' để thay thế. Đây là mã để tải một mô-đun.

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) 

Kiểm tra;

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