2017-09-08 6 views
4

필자는 동적으로 생성 된 유형 (즉, 3-arg type()을 호출하여 생성 된 유형)을 피클 링하고 멋지게 제거하려고 시도했습니다. 난 this module switching trick 모듈의 사용자로부터 세부 정보를 숨기고 깨끗한 의미를 부여하기 위해 사용 해왔다. 이미동적으로 생성 된 피킹

내가 배운 몇 가지 :

  1. 유형이 유형은 무엇 getattr 발견과 일치해야합니다
  2. 모듈 자체에 getattr로 검색 가능해야합니다, 그것은 우리가 pickle.dumps(o)를 호출하면 말을하는 것입니다 다음은 그래도 붙어있어 type(o) == getattr(module, 'name of type')

여전히 뭔가 이상한 일이있을 것 같은 것을 참이어야합니다 - 그것은 __getstate__를 호출하는 것으로 보인다 예기치 않은 일에.

다음은 파이썬 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)) 

다음주는 : 난 아무것도가 0123보고보고 기대하지 않은

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 

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__ 구현 피클을 통해 프로그래밍 방식으로 생성 된 유형을 만드는 것에 대해 어떻게 생각해야할까요?

f의 클래스 module.foo.bar.woof.last 피클을

+0

모듈 전환 트릭을 ​​사용해야하는 이유가 있습니까? 귀하의 예제를 좀 더 단순화 시켰고'module.Magic (''). foo.bar.woof.last()'와 잘 동작합니다. 모듈 스위치는'it is not same object' 에러를 발생시킵니다. 나는 아직도 이러한 모든 문제를 일으키는 세부 사항을 연구 중이다. – illiteratecoder

답변

1

f, pickle 요구를 피클하기 위해 (내가 뭔가를 생산하는 pickle.dumps를 얻기 위해 관리했습니다 분명 일단 내가 같은 일에 undump 전화를 pickle.loads 기대).

문서는 임의의 클래스 피클에 대한 지원을 요구하지 않습니다. They claim the following : 다음과 같은 유형의 절인 할 수

:

  • ...모듈

module.foo.bar.woof.last의 최상위에 정의

  • 클래스 모듈의 상위 레벨 module 등이라도 척 모듈에서 정의되지 않는다. 이하지-공식적으로 지원하는 경우, 피클 논리는, module.foo.bar.woof 피클하려고 끝 here 중 하나

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

    또는

    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.woofhere 여러 이유로 절인 할 수 없습니다. __getstate__과 같이 지원되지 않는 모든 메소드 조회에 대해 호출 할 수없는 Magic 인스턴스를 반환합니다.이 인스턴스는 첫 번째 오류가 발생한 곳입니다. 모듈을 전환하는 것은 Magic 클래스에서 피클 링을 찾아내는 것을 막습니다. 두 번째 오류가 발생한 곳입니다. 아마 더 많은 비 호환성이 있습니다.

  • 0

    마찬가지로, 이미 callable 클래스를 만드는 것이 다른 잘못된 방향을 드리프트하는 것일 뿐이라는 것을 알았습니다. 다행히도 this hack에 도달 했으므로 TYPE으로 클래스를 다시 만들 수있는 방법을 찾을 수있었습니다. <class 'module.Magic'>: it's not the same object as module.Magic 오류 컨텍스트에 따라 pickler는 다른 호출자와 다른 형식을 렌더링하는 동일한 호출을 반복하지 않습니다.이 인스턴스는 자체 instanciating 클래스의 주요 공통 문제입니다. 솔루션은 그 타입이 @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) 
    

    에도이 박쥐의 가장자리에서 공을 치는 아니 더 가정, 나는 덤프의 내용을 볼 수 있었다 내 콘솔에.