2012-11-15 18 views
16

के साथ अनियमित पाइथन ऑब्जेक्ट्स मैं एक अन्य पायथन परियोजना में एक सहयोगी द्वारा निर्मित एक परियोजना Project A को एकीकृत करने की कोशिश कर रहा हूं। अब इस सहयोगी अपने कोड में रिश्तेदार आयात इस्तेमाल नहीं किया, लेकिन इसके बजायएक बदले हुए मॉड्यूल पथ

from packageA.moduleA import ClassA 
from packageA.moduleA import ClassB 

किया और इसके परिणामस्वरूप cPickle साथ कक्षाएं मसालेदार। स्वच्छता के लिए मैं अपने प्रोजेक्ट के अंदर निर्मित पैकेज (Project A) पैकेज को छिपाना चाहता हूं। हालांकि यह packageA में परिभाषित कक्षाओं के पथ को बदलता है। कोई बात नहीं, मैं सिर्फ

from ..packageA.moduleA import ClassA 
from ..packageA.moduleA import ClassB 

का उपयोग कर आयात लेकिन अब संयुक्त राष्ट्र अचार बनाने कक्षाएं निम्न संदेश के साथ विफल रहता है

with open(fname) as infile: self.clzA = cPickle.load(infile) 
ImportError: No module named packageA.moduleA 

तो क्यों जाहिरा तौर पर मॉड्यूल defs देख नहीं cPickle है फिर से परिभाषित करेंगे। क्या मुझे सिस्टम पथ पर packageA की रूट जोड़ने की आवश्यकता है? क्या समस्या को हल करने का यह सही तरीका है?

cPickled फ़ाइल लग रहा है

ccopy_reg 
_reconstructor 
p1 
(cpackageA.moduleA 
ClassA 
p2 
c__builtin__ 
object 
p3 
NtRp4 

पुराने प्रोजेक्ट पदानुक्रम की तरह कुछ प्रकार

packageA/ 
    __init__.py 
    moduleA.py 
    moduleB.py 
packageB/ 
    __init__.py 
    moduleC.py 
    moduleD.py 

मैं एक WrapperPackage

MyPackage/ 
.. __init__.py 
.. myModuleX.py 
.. myModuleY.py 
WrapperPackage/ 
.. __init__.py 
.. packageA/ 
    .. __init__.py 
    .. moduleA.py 
    .. moduleB.py 
.. packageB/ 
    .. __init__.py 
    .. moduleC.py 
    .. moduleD.py 
+0

मैं इस समस्या KRunner के लिए एक प्लग-इन लिख बारे में जाना। प्लाज्मा द्वारा उपयोग किए गए स्क्रिप्ट इंजन ने एक नकली पैकेज बनाने के लिए पथ हुक का उपयोग किया जहां मेरा कोड था। दुर्भाग्यवश मुझे इसे हल करने का कोई तरीका नहीं मिला। एकमात्र चीज जो मैं कर सकता था वह था कि मैन्युअल रूप से अपने पथ हुक को हटा दें, 'sys' कैश साफ़ करें और सब कुछ पुनः आयात करें। लेकिन अगर आपके पास कुछ मसालेदार डेटा हैं तो आपको इसे उसी कक्षा के नाम से अनपिक करना होगा (जिसका अर्थ है कि आपको 'पैकेज से .m मॉड्यूलए आयात क्लास' रखना होगा)। ध्यान दें कि एक बार अनपिक्त होकर आप सही नाम का उपयोग कर उन्हें फिर से उठा सकते हैं। – Bakuriu

उत्तर

15
में यह सब करना चाहते हैं, की है

आपको पिकल आयात के लिए काम करने के लिए उपनाम बनाना होगा; WrapperPackage पैकेज के __init__.py फ़ाइल में निम्नलिखित:

from .packageA import * # Ensures that all the modules have been loaded in their new locations *first*. 
from . import packageA # imports WrapperPackage/packageA 
import sys 
sys.modules['packageA'] = packageA # creates a packageA entry in sys.modules 

हो सकता है कि आप हालांकि अतिरिक्त प्रविष्टियाँ बनाने की आवश्यकता होगी:

sys.modules['packageA.moduleA'] = moduleA 
# etc. 

अब cPickle अपने पुराने पर फिर से packageA.moduleA और packageA.moduleB मिलेगा स्थानों।

आप बाद में अचार फ़ाइल को फिर से लिखना चाहेंगे, उस समय नए मॉड्यूल स्थान का उपयोग किया जाएगा। ऊपर बनाए गए अतिरिक्त उपनामों को यह सुनिश्चित करना चाहिए कि प्रश्नों के मॉड्यूल में कक्षाओं को फिर से लिखने के लिए cPickle के लिए नया स्थान नाम हो।

+0

क्या मुझे इसे 'WrapperPackege .__ init __। Py' में करने की ज़रूरत है? –

+0

@MattiLyra: आप इसे कहीं भी कर सकते हैं, लेकिन 'WrapperPackage/__ init __। Py' फ़ाइल शायद सबसे अच्छी जगह है। –

+0

@MartinPieters PEP328 'आयात के अनुसार। कुछ 'अमान्य है, इसे' कुछ आयात मॉड्यूल 'से होना चाहिए? 'आयात। कुछ' 'सिंटेक्स त्रुटि 'फेंकता है? http://www.python.org/dev/peps/pep-0328/#guido-s-decision –

4

@MartinPieters के अलावा उत्तर देने के ऐसा करने का दूसरा रास्ता cPickle.Unpickler वर्ग के find_global विधि निर्धारित करें, या pickle.Unpickler वर्ग का विस्तार करने के लिए है।

def map_path(mod_name, kls_name): 
    if mod_name.startswith('packageA'): # catch all old module names 
     mod = __import__('WrapperPackage.%s'%mod_name, fromlist=[mod_name]) 
     return getattr(mod, kls_name) 
    else: 
     mod = __import__(mod_name) 
     return getattr(mod, kls_name) 

import cPickle as pickle 
with open('dump.pickle','r') as fh: 
    unpickler = pickle.Unpickler(fh) 
    unpickler.find_global = map_path 
    obj = unpickler.load() # object will now contain the new class path reference 

with open('dump-new.pickle','w') as fh: 
    pickle.dump(obj, fh) # ClassA will now have a new path in 'dump-new' 

pickle और cPickle दोनों के लिए प्रक्रिया का एक अधिक विस्तृत विवरण here पाया जा सकता है।

+0

लिंक की गई वेबसाइट ऑफ़लाइन है, लेकिन archive.org में यह है [यहां] (https://web-beta.archive.org/web/20130423223601/http://nadiana.com/python-pickle-insecure), और यह पढ़ने के लायक है। –

2

एक संभावित समाधान सीधे पिक फ़ाइल को संपादित करना है (यदि आपके पास पहुंच है)। मैं एक बदले गए मॉड्यूल पथ की एक ही समस्या में भाग गया, और मैंने फ़ाइलों को अचार के रूप में सहेजा था। HIGHEST_PROTOCOL इसलिए यह सिद्धांत में बाइनरी होना चाहिए, लेकिन मॉड्यूल पथ सादे पाठ में अचार फ़ाइल के शीर्ष पर बैठा था। इसलिए मैंने पुराने मॉड्यूल पथ के सभी उदाहरणों पर नए और वॉयला के साथ एक खोज को बदल दिया, वे सही ढंग से लोड हो गए।

मुझे यकीन है कि यह समाधान सभी के लिए नहीं है, खासकर यदि आपके पास एक जटिल जटिल मसालेदार वस्तु है, लेकिन यह एक त्वरित और गंदे डेटा फिक्स है जो मेरे लिए काम करता है!

0

लचीला अनपिकलिंग के लिए यह मेरा मूलभूत पैटर्न है - एक अस्पष्ट और तेज़ संक्रमण मानचित्र के माध्यम से - आमतौर पर पिकलिंग के लिए प्रासंगिक प्राचीन डेटा-प्रकारों के अलावा कुछ ज्ञात वर्ग होते हैं। यह ग़लत या दुर्भावनापूर्ण रूप से निर्मित डेटा के खिलाफ अनपिकलिंग की भी रक्षा करता है, जो बाद में सरल pickle.load() (त्रुटि-प्रवण sys.modules fiddling के साथ या बिना) पर मनमाने ढंग से पायथन कोड (!) निष्पादित कर सकता है।

अजगर 2 & 3:

from __future__ import print_function 
try: import cPickle as pickle, copy_reg as copyreg 
except: import pickle, copyreg 

class OldZ: 
    a = 1 
class Z(object): 
    a = 2 
class Dangerous: 
    pass 

_unpickle_map_safe = { 
    # all possible and allowed (!) classes & upgrade paths  
    (__name__, 'Z')   : Z,  
    (__name__, 'OldZ')  : Z, 
    ('old.package', 'OldZ') : Z, 
    ('__main__', 'Z')  : Z, 
    ('__main__', 'OldZ') : Z, 
    # basically required 
    ('copy_reg', '_reconstructor') : copyreg._reconstructor,  
    ('__builtin__', 'object')  : copyreg._reconstructor,  
    } 

def unpickle_find_class(modname, clsname): 
    print("DEBUG unpickling: %(modname)s . %(clsname)s" % locals()) 
    try: return _unpickle_map_safe[(modname, clsname)] 
    except KeyError: 
     raise pickle.UnpicklingError(
      "%(modname)s . %(clsname)s not allowed" % locals()) 
if pickle.__name__ == 'cPickle': # PY2 
    def SafeUnpickler(f): 
     u = pickle.Unpickler(f) 
     u.find_global = unpickle_find_class 
     return u 
else: # PY3 & Python2-pickle.py 
    class SafeUnpickler(pickle.Unpickler): 
     find_class = staticmethod(unpickle_find_class) 

def test(fn='./z.pkl'): 
    z = OldZ() 
    z.b = 'teststring' + sys.version 
    pickle.dump(z, open(fn, 'wb'), 2) 
    pickle.dump(Dangerous(), open(fn + 'D', 'wb'), 2) 
    # load again 
    o = SafeUnpickler(open(fn, 'rb')).load() 
    print(pickle, "loaded:", o, o.a, o.b) 
    assert o.__class__ is Z 
    try: raise SafeUnpickler(open(fn + 'D', 'rb')).load() and AssertionError 
    except pickle.UnpicklingError: print('OK: Dangerous not allowed') 

if __name__ == '__main__': 
    test() 
संबंधित मुद्दे