2016-04-19 5 views
10

के लिए आयात हुक I के कुछ समान दृष्टिकोण में sys.meta_path के माध्यम से कुछ import hooks सेट करने का प्रयास कर रहा हूं। इसके लिए, मुझे उपरोक्त लिंक में बताए गए अनुसार दो कार्यों को find_module और load_module परिभाषित करने की आवश्यकता है। यहाँ मेरी load_module समारोह,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 

जो सबसे मॉड्यूल के लिए ठीक काम करता है, लेकिन PyQt4.QtCore के लिए विफल रहता है जब अजगर 2.7 का उपयोग कर:

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 

एक ही कोड काम करता है पायथन 3.4 के साथ ठीक है (हालांकि imp बहिष्कृत हो रहा है और importlib आदर्श रूप से इसके बजाय उपयोग किया जाना चाहिए)।

मुझे लगता है कि एसआईपी गतिशील मॉड्यूल प्रारंभिकरण के साथ इसका कुछ संबंध है। क्या कुछ और है जो मुझे पायथन 2.7 के साथ आज़माएं?

नोट: यह PyQt4 और PyQt5 दोनों के साथ लागू होता है।

संपादित: इस 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 का उपयोग करें। हालांकि, यह स्पष्ट नहीं है कि पाइथन 2 में एक डिफ़ॉल्ट load_module कार्यान्वयन कहाँ प्राप्त करें। निकटतम कार्यान्वयन मैंने कुछ ऐसा पाया है जो future.standard_library.RenameImport है। ऐसा लगता है कि पाइथन 3 से 2. के पूर्ण कार्यान्वयन का बैकपोर्ट नहीं है।

इस समस्या को पुन: उत्पन्न करने वाले आयात हुक के लिए एक न्यूनतम कार्य उदाहरण इस gist में पाया जा सकता है।

+0

यह उपयोगी हो सकता है, तो मुझे क्या करना है कोशिश कर रहा हूँ, [SiQt] (https://github.com/rth/SiQt) पैकेज को देखने के लिए कुछ सामान्य संदर्भ देने के लिए, और इस समस्या है [इस जिथब मुद्दे] में चर्चा की गई (https://github.com/rth/SiQt/issues/4)। – rth

+0

मैं वास्तव में आपकी समस्या को समझ नहीं पा रहा हूं लेकिन '__import __ ('PyQt4.QtCore') के साथ क्या गलत है। क्या यह अनंत रिकर्सन का कारण बनता है? – danidee

+0

@danidee '__import __ ('ए') 'के साथ कुछ भी गलत नहीं है, लेकिन यह' आयात ए 'का उपयोग करने के बराबर है। मैं जो चाहता हूं उसे बदलना है, जब आप ऐसा करते हैं, और विशेष रूप से 'आयात बी' चलाते हैं, जब आप 'ए आयात करते हैं'। यह 'sys.meta_path' में आयात हुक के साथ किया जा सकता है, लेकिन उन्हें 'imp.load_module' जैसे निम्न स्तर के कार्यों की आवश्यकता होती है। – rth

उत्तर

4

यूपीडी: यह हिस्सा उत्तर अपडेट के बाद वास्तव में प्रासंगिक नहीं है, इसलिए नीचे यूपीडी देखें।

क्यों न सिर्फ importlib.import_module, जो दोनों अजगर 2.7 और अजगर 3 में उपलब्ध है का उपयोग करें:

#test.py 

import importlib 

mod = importlib.import_module('PyQt4.QtCore') 
print(mod.__file__) 
Ubuntu 14.04 पर

:

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

चूंकि यह एक गतिशील मॉड्यूल है, में कहा के रूप में त्रुटि (और वास्तविक फ़ाइल QtCore.so है), imp.load_dynamic पर भी एक नज़र डालें।

एक और समाधान मॉड्यूल प्रारंभिक कोड के निष्पादन को मजबूर करने के लिए हो सकता है, लेकिन आईएमओ यह एक परेशानी का बहुत अधिक है, तो क्यों न केवल importlib का उपयोग करें।

यूपीडी: pkgutil में चीजें हैं, जो मदद कर सकती हैं। मुझे अपने टिप्पणी में के बारे में बात कर रहा था, इस तरह से अपनी खोजक संशोधित करने के लिए प्रयास करें:

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 

अब ऊपर कोड का परीक्षण नहीं किया जा सकता है, तो यह अपने आप कोशिश करें।

पीएस ध्यान दें कि imp.load_module(), जो आपके लिए पाइथन 3 में काम करता है 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__ 

उत्पादन:

<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 को रखा, और pickle के साथ डिफ़ॉल्ट मशीनरी द्वारा आयात किया जाता है परिवर्तनीय नाम shelve

+0

मैं आपके दो पहले विचारों से सहमत हूं, दुर्भाग्य से वे काम नहीं करते हैं, मैंने पहले कोशिश की है। ए) जहां तक ​​मैं समझता हूं, 'importlib.import_module'' sys.meta_path' आयात हुक में डालने के लिए बहुत अधिक स्तर है। क्या होता है जब आप एक पैकेज आयात करते हैं तो यह 'sys.meta_path' में दिखाई देगा, और यदि 'load_module' फ़ंक्शन' importlib.import_module' का उपयोग करता है, तो यह फिर से sys.meta_path' में दिखाई देगा जहां यह वही' load_module' फ़ंक्शन इत्यादि, इसलिए आपको एक अनंत रिकर्सन समस्या मिलती है ... 'le.find_module' या' importlib.machinery.SourceFileLoader – rth

+0

बी) जैसे कम लीवर की आवश्यकता है। मैंने 'imp।load_dynamic', यह एक ही परिणाम उत्पन्न करता है (चूंकि इसे 'imp.load_module' द्वारा माना जाना चाहिए)। सी) हां, मुझे पता है कि मैं उस मॉड्यूल को हाथ से शुरू नहीं करना चाहता हूं। मुझे समझ में नहीं आता है कि मुझे क्यों करना है (यानी ऑपरेशन 'importlib.import_module' क्या करता है और' imp.load_module' करता है, जो यह आवश्यक बनाता है)। सभी PyQt4/PyQt4 submodules के लिए भी यही सच है। जो मैं हासिल करने की कोशिश कर रहा हूं वह 'SiQt.QtCore' आयात करता है जब' PyQt4.QtCore'is आयात किया जाता है। मुझे पता है कि यह पाइथन भविष्य के बाद से संभव है। Standard_library.RenameImport इसे PY2 में करता है (अनिवार्य रूप से यह केवल आयात नामकरण है)। – rth

+1

@rth, आपके द्वारा आयात हुक के बारे में प्रदान किए गए लिंक से, यह कहता है कि मेटा पथ खोजक मार्ग के प्रत्येक भाग के लिए 'find_spec'/'find_module' को रिकर्सिव रूप से कॉल करेगा। जैसे 'mpf.find_spec (" PyQt4 ", कोई नहीं, कोई नहीं) 'और फिर एक और' mpf.find_spec (" PyQt4.QtCore ", PyQt4 .__ पथ__, कोई नहीं)'। तो यदि आप 'find_spec' या mpf के किसी अन्य भाग में हुकिंग कर रहे हैं, तो नाम स्ट्रिंग में 'SiQt' के साथ' PyQt4' को प्रतिस्थापित किया जा सकता है और फिर डिफ़ॉल्ट मशीनरी को इसे '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 
संबंधित मुद्दे