腌制动态创建的类型

时间:2017-09-08 21:33:54

标签: python pickle

我一直在努力获得一些动态创建的类型(即通过调用3-arg type()创建的类型)来进行pickle和unpickle。我一直在使用this module switching trick隐藏模块用户的详细信息,并提供干净的语义。

我已经学到了很多东西:

  1. 必须在模块本身getattr上找到该类型
  2. 该类型必须与getattr找到的内容一致,也就是说,如果我们调用pickle.dumps(o),则必须为type(o) == getattr(module, 'name of type')
  3. 我被困住的地方似乎仍有一些奇怪的事情发生 - 它似乎是在意外的事情上调用__getstate__

    这是我用最简单的设置重现问题,使用Python 3.5进行测试,但如果可能的话我想回到3.3:

    # module.py
    import sys
    import functools
    
    def dump(self):
        return b'Some data' # Dummy for testing
    
    def undump(self, data):
        print('Undump: %r' % data) # Do nothing for testing
    
    # Cheaty demo way to make this consistent
    @functools.lru_cache(maxsize=None)
    def make_type(name):
        return type(name, (), {
            '__getstate__': dump,
            '__setstate__': undump,
        })
    
    class Magic(object):
        def __init__(self, path):
            self.path = path
    
        def __getattr__(self, name):
            print('Getting thing: %s (from: %s)' % (name, self.path))
            # for simple testing all calls to make_type must end in last x.y.z.last
            if name != 'last':
                if self.path:
                    return Magic(self.path + '.' + name)
                else:
                    return Magic(name)
            return make_type(self.path + '.' + name)
    
    # Make the switch
    sys.modules[__name__] = Magic('')
    

    然后快速练习:

    import module
    import pickle
    
    f=module.foo.bar.woof.last()
    print(f.__getstate__()) # See, *this* works
    print('Pickle starts here')
    print(pickle.dumps(f))
    

    然后给出:

    Getting thing: foo (from: )
    Getting thing: bar (from: foo)
    Getting thing: woof (from: foo.bar)
    Getting thing: last (from: foo.bar.woof)
    b'Some data'
    Pickle starts here
    Getting thing: __spec__ (from: )
    Getting thing: _initializing (from: __spec__)
    Getting thing: foo (from: )
    Getting thing: bar (from: foo)
    Getting thing: woof (from: foo.bar)
    Getting thing: last (from: foo.bar.woof)
    Getting thing: __getstate__ (from: foo.bar.woof)
    Traceback (most recent call last):
      File "test.py", line 7, in <module>
        print(pickle.dumps(f))
    TypeError: 'Magic' object is not callable
    

    我不希望在__getstate__上看到任何查找module.foo.bar.woof的内容,但即使我们通过添加强制查找失败:

    if name == '__getstate__': raise AttributeError()
    

    进入我们的__getattr__它仍然失败:

    Traceback (most recent call last):
      File "test.py", line 7, in <module>
        print(pickle.dumps(f))
    _pickle.PicklingError: Can't pickle <class 'module.Magic'>: it's not the same object as module.Magic
    

    是什么给出的?我错过了__spec__的内容吗? docs for __spec__几乎只是强调设置它,但似乎并没有真正解释太多。

    更重要的是,更大的问题是我应该如何通过伪模块的__getattr__实现pickle以编程方式生成类型?

    (很显然,一旦我设法让pickle.dumps生成我希望pickle.loads用同样的东西调用undump的内容,我可以使用public static async Task<string> GetAccessTokenAsync(string clientId, string appKey, string resourceId) { string aadInstance = "https://login.microsoftonline.com/{0}"; string tenant = "<TENANT>.onmicrosoft.com"; string authority = string.Format(CultureInfo.InvariantCulture, aadInstance, tenant); AuthenticationContext authContext = new AuthenticationContext(authority); ClientCredential clientCredential = new ClientCredential(clientId, appKey); AuthenticationResult authenticationResult = null; authenticationResult = await authContext.AcquireTokenAsync(resourceId, clientCredential); return authenticationResult.AccessToken; }

2 个答案:

答案 0 :(得分:1)

要挑选fpickle需要挑选f的课程module.foo.bar.woof.last

文档并不声称支持任意课程。 They claim the following

  

可以腌制以下类型:

     
      
  • ...
  •   
  • 在模块顶层定义的类
  •   

module.foo.bar.woof.last未定义在模块的顶层,甚至是module之类的假装模块。在这个未得到官方支持的案例中,pickle逻辑最终试图挑选module.foo.bar.woofhere

    elif parent is not module:
        self.save_reduce(getattr, (parent, lastname))

here

    else if (parent != module) {
        PickleState *st = _Pickle_GetGlobalState();
        PyObject *reduce_value = Py_BuildValue("(O(OO))",
                                    st->getattr, parent, lastname);
        status = save_reduce(self, reduce_value, NULL);
由于多种原因,

module.foo.bar.woof无法被腌制。它为所有不受支持的方法查找返回一个不可调用的Magic实例,例如__getstate__,这是您的第一个错误来源。模块切换事情阻止找到Magic类来挑选它,这是你的第二个错误来自的地方。可能存在更多不兼容性。

答案 1 :(得分:0)

看起来,并且已经证明,使callable课程只是一个漂移的另一个错误的方向,谢天谢地this hack,我可以找到一个解决方法,使该类可以通过其TYPE重复。在错误<class 'module.Magic'>: it's not the same object as module.Magic的上下文之后,pickler不会迭代通过相同的调用来呈现与另一个类型不同的类型,这是一个主要的常见问题,即对于这个实例,pickle self instanciating classes它的类,解决方案是使用类型@mock.patch('module.Magic', type(module.Magic))修补类这是一个简短的答案。

<强> Main.py

import module
import pickle
import mock


f=module1.foo.bar.woof.last
print(f().__getstate__()) # See, *this* works
print('Pickle starts here')
@mock.patch('module1.Magic', type(module1.Magic))
def pickleit():
    return pickle.dumps(f())
print(pickleit())

魔术课

class Magic(object):

    def __init__(self, value):
        self.path = value

    __class__: lambda x:x

    def __getstate__(self):
        print ("Shoot me! i'm at " +  self.path )
        return dump(self)

   def __setstate__(self,value):
        print ('something will never occur')
        return undump(self,value)

    def __spec__(self):
        print ("Wrong side of the planet ")

    def _initializing(self):
        print ("Even farther lost ")

     def __getattr__(self, name):
        print('Getting thing: %s (from: %s)' % (name, self.path))
        # for simple testing all calls to make_type must end in last x.y.z.last
        if name != 'last':
            if self.path:
                return Magic(self.path + '.' + name)
            else:
                return Magic(name)
        print('terminal stage' )
        return make_type(self.path + '.' + name)

即使假设这不是击球的边缘,我也可以看到内容被丢弃到我的控制台中。