Changeset 147


Ignore:
Timestamp:
04/22/06 16:40:43 (9 years ago)
Author:
xi
Message:

Add support for pickling/unpickling python objects.

Location:
pyyaml/trunk
Files:
2 added
5 edited

Legend:

Unmodified
Added
Removed
  • pyyaml/trunk/lib/yaml/__init__.py

    r146 r147  
    232232    Dumper.add_representer(data_type, representer) 
    233233 
     234def add_multi_representer(data_type, multi_representer, Dumper=Dumper): 
     235    """ 
     236    Add a representer for the given type. 
     237    Multi-representer is a function accepting a Dumper instance 
     238    and an instance of the given data type or subtype 
     239    and producing the corresponding representation node. 
     240    """ 
     241    Dumper.add_multi_representer(data_type, multi_representer) 
     242 
    234243class YAMLObjectMetaclass(type): 
    235244    """ 
  • pyyaml/trunk/lib/yaml/constructor.py

    r146 r147  
    493493        return self.find_python_module(suffix, node.start_mark) 
    494494 
     495    class classobj: pass 
     496 
     497    def make_python_instance(self, suffix, node, 
     498            args=None, kwds=None, newobj=False): 
     499        if not args: 
     500            args = [] 
     501        if not kwds: 
     502            kwds = {} 
     503        cls = self.find_python_name(suffix, node.start_mark) 
     504        if newobj and isinstance(cls, type(self.classobj))  \ 
     505                and not args and not kwds: 
     506            instance = self.classobj() 
     507            instance.__class__ = cls 
     508            return instance 
     509        elif newobj and isinstance(cls, type): 
     510            return cls.__new__(cls, *args, **kwds) 
     511        else: 
     512            return cls(*args, **kwds) 
     513 
     514    def set_python_instance_state(self, instance, state): 
     515        if hasattr(instance, '__setstate__'): 
     516            instance.__setstate__(state) 
     517        else: 
     518            slotstate = {} 
     519            if isinstance(state, tuple) and len(state) == 2: 
     520                state, slotstate = state 
     521            if hasattr(instance, '__dict__'): 
     522                instance.__dict__.update(state) 
     523            elif state: 
     524                slotstate.update(state) 
     525            for key, value in slotstate.items(): 
     526                setattr(object, key, value) 
     527 
     528    def construct_python_object(self, suffix, node): 
     529        # Format: 
     530        #   !!python/object:module.name { ... state ... } 
     531        instance = self.make_python_instance(suffix, node, newobj=True) 
     532        state = self.construct_mapping(node) 
     533        self.set_python_instance_state(instance, state) 
     534        return instance 
     535 
     536    def construct_python_object_apply(self, suffix, node, newobj=False): 
     537        # Format: 
     538        #   !!python/object/apply       # (or !!python/object/new) 
     539        #   args: [ ... arguments ... ] 
     540        #   kwds: { ... keywords ... } 
     541        #   state: ... state ... 
     542        #   listitems: [ ... listitems ... ] 
     543        #   dictitems: { ... dictitems ... } 
     544        # or short format: 
     545        #   !!python/object/apply [ ... arguments ... ] 
     546        # The difference between !!python/object/apply and !!python/object/new 
     547        # is how an object is created, check make_python_instance for details. 
     548        if isinstance(node, SequenceNode): 
     549            args = self.construct_sequence(node) 
     550            kwds = {} 
     551            state = {} 
     552            listitems = [] 
     553            dictitems = {} 
     554        else: 
     555            value = self.construct_mapping(node) 
     556            args = value.get('args', []) 
     557            kwds = value.get('kwds', {}) 
     558            state = value.get('state', {}) 
     559            listitems = value.get('listitems', []) 
     560            dictitems = value.get('dictitems', {}) 
     561        instance = self.make_python_instance(suffix, node, args, kwds, newobj) 
     562        if state: 
     563            self.set_python_instance_state(instance, state) 
     564        if listitems: 
     565            instance.extend(listitems) 
     566        if dictitems: 
     567            for key in dictitems: 
     568                instance[key] = dictitems[key] 
     569        return instance 
     570 
     571    def construct_python_object_new(self, suffix, node): 
     572        return self.construct_python_object_apply(suffix, node, newobj=True) 
     573 
     574 
    495575Constructor.add_constructor( 
    496576    u'tag:yaml.org,2002:python/none', 
     
    545625    Constructor.construct_python_module) 
    546626 
     627Constructor.add_multi_constructor( 
     628    u'tag:yaml.org,2002:python/object:', 
     629    Constructor.construct_python_object) 
     630 
     631Constructor.add_multi_constructor( 
     632    u'tag:yaml.org,2002:python/object/apply:', 
     633    Constructor.construct_python_object_apply) 
     634 
     635Constructor.add_multi_constructor( 
     636    u'tag:yaml.org,2002:python/object/new:', 
     637    Constructor.construct_python_object_new) 
     638 
  • pyyaml/trunk/lib/yaml/representer.py

    r143 r147  
    1717    from sets import Set as set 
    1818 
    19 import sys 
     19import sys, copy_reg 
    2020 
    2121class RepresenterError(YAMLError): 
     
    2525 
    2626    yaml_representers = {} 
     27    yaml_multi_representers = {} 
    2728 
    2829    def __init__(self): 
     
    3031 
    3132    def represent(self, data): 
    32         node = self.represent_object(data) 
     33        node = self.represent_data(data) 
    3334        self.serialize(node) 
    3435        self.represented_objects = {} 
     
    5051        return bases 
    5152 
    52     def represent_object(self, data): 
     53    def represent_data(self, data): 
    5354        if self.ignore_aliases(data): 
    5455            alias_key = None 
     
    6566        if type(data) is self.instance_type: 
    6667            data_types = self.get_classobj_bases(data.__class__)+list(data_types) 
    67         for data_type in data_types: 
    68             if data_type in self.yaml_representers: 
    69                 node = self.yaml_representers[data_type](self, data) 
    70                 break 
    71         else: 
    72             if None in self.yaml_representers: 
    73                 node = self.yaml_representers[None](self, data) 
     68        if data_types[0] in self.yaml_representers: 
     69            node = self.yaml_representers[data_types[0]](self, data) 
     70        else: 
     71            for data_type in data_types: 
     72                if data_type in self.yaml_multi_representers: 
     73                    node = self.yaml_multi_representers[data_type](self, data) 
     74                    break 
    7475            else: 
    75                 node = ScalarNode(None, unicode(data)) 
     76                if None in self.yaml_multi_representers: 
     77                    node = self.yaml_multi_representers[None](self, data) 
     78                elif None in self.yaml_representers: 
     79                    node = self.yaml_representers[None](self, data) 
     80                else: 
     81                    node = ScalarNode(None, unicode(data)) 
    7682        if alias_key is not None: 
    7783            self.represented_objects[alias_key] = node 
     
    8490    add_representer = classmethod(add_representer) 
    8591 
     92    def add_multi_representer(cls, data_type, representer): 
     93        if not 'yaml_multi_representers' in cls.__dict__: 
     94            cls.yaml_multi_representers = cls.yaml_multi_representers.copy() 
     95        cls.yaml_multi_representers[data_type] = representer 
     96    add_multi_representer = classmethod(add_multi_representer) 
     97 
    8698    def represent_scalar(self, tag, value, style=None): 
    8799        return ScalarNode(tag, value, style=style) 
    88100 
    89101    def represent_sequence(self, tag, sequence, flow_style=None): 
     102        best_style = True 
    90103        value = [] 
    91104        for item in sequence: 
    92             value.append(self.represent_object(item)) 
     105            node_item = self.represent_data(item) 
     106            if not (isinstance(node_item, ScalarNode) and not node_item.style): 
     107                best_style = False 
     108            value.append(self.represent_data(item)) 
     109        if flow_style is None: 
     110            flow_style = best_style 
    93111        return SequenceNode(tag, value, flow_style=flow_style) 
    94112 
    95113    def represent_mapping(self, tag, mapping, flow_style=None): 
     114        best_style = True 
    96115        if hasattr(mapping, 'keys'): 
    97116            value = {} 
    98117            for item_key in mapping.keys(): 
    99118                item_value = mapping[item_key] 
    100                 value[self.represent_object(item_key)] =    \ 
    101                         self.represent_object(item_value) 
     119                node_key = self.represent_data(item_key) 
     120                node_value = self.represent_data(item_value) 
     121                if not (isinstance(node_key, ScalarNode) and not node_key.style): 
     122                    best_style = False 
     123                if not (isinstance(node_value, ScalarNode) and not node_value.style): 
     124                    best_style = False 
     125                value[node_key] = node_value 
    102126        else: 
    103127            value = [] 
    104128            for item_key, item_value in mapping: 
    105                 value.append((self.represent_object(item_key), 
    106                         self.represent_object(item_value))) 
     129                node_key = self.represent_data(item_key) 
     130                node_value = self.represent_data(item_value) 
     131                if not (isinstance(node_key, ScalarNode) and not node_key.style): 
     132                    best_style = False 
     133                if not (isinstance(node_value, ScalarNode) and not node_value.style): 
     134                    best_style = False 
     135                value.append((node_key, node_value)) 
     136        if flow_style is None: 
     137            flow_style = best_style 
    107138        return MappingNode(tag, value, flow_style=flow_style) 
    108139 
     
    259290 
    260291class Representer(SafeRepresenter): 
    261      
     292 
    262293    def represent_str(self, data): 
    263294        tag = None 
     
    313344                u'tag:yaml.org,2002:python/module:'+data.__name__, u'') 
    314345 
     346    def represent_instance(self, data): 
     347        # For instances of classic classes, we use __getinitargs__ and 
     348        # __getstate__ to serialize the data. 
     349 
     350        # If data.__getinitargs__ exists, the object must be reconstructed by 
     351        # calling cls(**args), where args is a tuple returned by 
     352        # __getinitargs__. Otherwise, the cls.__init__ method should never be 
     353        # called and the class instance is created by instantiating a trivial 
     354        # class and assigning to the instance's __class__ variable. 
     355 
     356        # If data.__getstate__ exists, it returns the state of the object. 
     357        # Otherwise, the state of the object is data.__dict__. 
     358 
     359        # We produce either a !!python/object or !!python/object/new node. 
     360        # If data.__getinitargs__ does not exist and state is a dictionary, we 
     361        # produce a !!python/object node . Otherwise we produce a 
     362        # !!python/object/new node. 
     363 
     364        cls = data.__class__ 
     365        class_name = u'%s.%s' % (cls.__module__, cls.__name__) 
     366        args = None 
     367        state = None 
     368        if hasattr(data, '__getinitargs__'): 
     369            args = list(data.__getinitargs__()) 
     370        if hasattr(data, '__getstate__'): 
     371            state = data.__getstate__() 
     372        else: 
     373            state = data.__dict__ 
     374        if args is None and isinstance(state, dict): 
     375            return self.represent_mapping( 
     376                    u'tag:yaml.org,2002:python/object:'+class_name, state) 
     377        if isinstance(state, dict) and not state: 
     378            return self.represent_sequence( 
     379                    u'tag:yaml.org,2002:python/object/new:'+class_name, args) 
     380        value = {} 
     381        if args: 
     382            value['args'] = args 
     383        value['state'] = state 
     384        return self.represent_mapping( 
     385                u'tag:yaml.org,2002:python/object/new:'+class_name, value) 
     386 
     387    def represent_object(self, data): 
     388        # We use __reduce__ API to save the data. data.__reduce__ returns 
     389        # a tuple of length 2-5: 
     390        #   (function, args, state, listitems, dictitems) 
     391 
     392        # For reconstructing, we calls function(*args), then set its state, 
     393        # listitems, and dictitems if they are not None. 
     394 
     395        # A special case is when function.__name__ == '__newobj__'. In this 
     396        # case we create the object with args[0].__new__(*args). 
     397 
     398        # Another special case is when __reduce__ returns a string - we don't 
     399        # support it. 
     400 
     401        # We produce a !!python/object, !!python/object/new or 
     402        # !!python/object/apply node. 
     403 
     404        cls = type(data) 
     405        if cls in copy_reg.dispatch_table: 
     406            reduce = copy_reg.dispatch_table[cls] 
     407        elif hasattr(data, '__reduce_ex__'): 
     408            reduce = data.__reduce_ex__(2) 
     409        elif hasattr(data, '__reduce__'): 
     410            reduce = data.__reduce__() 
     411        else: 
     412            raise RepresenterError("cannot represent object: %r" % data) 
     413        reduce = (list(reduce)+[None]*5)[:5] 
     414        function, args, state, listitems, dictitems = reduce 
     415        args = list(args) 
     416        if state is None: 
     417            state = {} 
     418        if listitems is not None: 
     419            listitems = list(listitems) 
     420        if dictitems is not None: 
     421            dictitems = dict(dictitems) 
     422        if function.__name__ == '__newobj__': 
     423            function = args[0] 
     424            args = args[1:] 
     425            tag = u'tag:yaml.org,2002:python/object/new:' 
     426            newobj = True 
     427        else: 
     428            tag = u'tag:yaml.org,2002:python/object/apply:' 
     429            newobj = False 
     430        function_name = u'%s.%s' % (function.__module__, function.__name__) 
     431        if not args and not listitems and not dictitems \ 
     432                and isinstance(state, dict) and newobj: 
     433            return self.represent_mapping( 
     434                    u'tag:yaml.org,2002:python/object:'+function_name, state) 
     435        if not listitems and not dictitems  \ 
     436                and isinstance(state, dict) and not state: 
     437            return self.represent_sequence(tag+function_name, args) 
     438        value = {} 
     439        if args: 
     440            value['args'] = args 
     441        if state or not isinstance(state, dict): 
     442            value['state'] = state 
     443        if listitems: 
     444            value['listitems'] = listitems 
     445        if dictitems: 
     446            value['dictitems'] = dictitems 
     447        return self.represent_mapping(tag+function_name, value) 
     448 
    315449Representer.add_representer(str, 
    316450        Representer.represent_str) 
     
    343477        Representer.represent_module) 
    344478 
     479Representer.add_multi_representer(Representer.instance_type, 
     480        Representer.represent_instance) 
     481 
     482Representer.add_multi_representer(object, 
     483        Representer.represent_object) 
     484 
  • pyyaml/trunk/tests/test_constructor.py

    r146 r147  
    113113        else: 
    114114            return False 
     115 
     116class AnObject(object): 
     117 
     118    def __new__(cls, foo=None, bar=None, baz=None): 
     119        self = object.__new__(cls) 
     120        self.foo = foo 
     121        self.bar = bar 
     122        self.baz = baz 
     123        return self 
     124 
     125    def __cmp__(self, other): 
     126        return cmp((type(self), self.foo, self.bar, self.baz), 
     127                (type(other), other.foo, other.bar, other.baz)) 
     128 
     129    def __eq__(self, other): 
     130        return type(self) is type(other) and    \ 
     131                (self.foo, self.bar, self.baz) == (other.foo, other.bar, other.baz) 
     132 
     133class AnInstance: 
     134 
     135    def __init__(self, foo=None, bar=None, baz=None): 
     136        self.foo = foo 
     137        self.bar = bar 
     138        self.baz = baz 
     139 
     140    def __cmp__(self, other): 
     141        return cmp((type(self), self.foo, self.bar, self.baz), 
     142                (type(other), other.foo, other.bar, other.baz)) 
     143 
     144    def __eq__(self, other): 
     145        return type(self) is type(other) and    \ 
     146                (self.foo, self.bar, self.baz) == (other.foo, other.bar, other.baz) 
     147 
     148class AState(AnInstance): 
     149 
     150    def __getstate__(self): 
     151        return { 
     152            '_foo': self.foo, 
     153            '_bar': self.bar, 
     154            '_baz': self.baz, 
     155        } 
     156 
     157    def __setstate__(self, state): 
     158        self.foo = state['_foo'] 
     159        self.bar = state['_bar'] 
     160        self.baz = state['_baz'] 
     161 
     162class ACustomState(AnInstance): 
     163 
     164    def __getstate__(self): 
     165        return (self.foo, self.bar, self.baz) 
     166 
     167    def __setstate__(self, state): 
     168        self.foo, self.bar, self.baz = state 
     169 
     170class InitArgs(AnInstance): 
     171 
     172    def __getinitargs__(self): 
     173        return (self.foo, self.bar, self.baz) 
     174 
     175    def __getstate__(self): 
     176        return {} 
     177 
     178class InitArgsWithState(AnInstance): 
     179 
     180    def __getinitargs__(self): 
     181        return (self.foo, self.bar) 
     182 
     183    def __getstate__(self): 
     184        return self.baz 
     185 
     186    def __setstate__(self, state): 
     187        self.baz = state 
     188 
     189class NewArgs(AnObject): 
     190 
     191    def __getnewargs__(self): 
     192        return (self.foo, self.bar, self.baz) 
     193 
     194    def __getstate__(self): 
     195        return {} 
     196 
     197class NewArgsWithState(AnObject): 
     198 
     199    def __getnewargs__(self): 
     200        return (self.foo, self.bar) 
     201 
     202    def __getstate__(self): 
     203        return self.baz 
     204 
     205    def __setstate__(self, state): 
     206        self.baz = state 
     207 
     208class Reduce(AnObject): 
     209 
     210    def __reduce__(self): 
     211        return self.__class__, (self.foo, self.bar, self.baz) 
     212 
     213class ReduceWithState(AnObject): 
     214 
     215    def __reduce__(self): 
     216        return self.__class__, (self.foo, self.bar), self.baz 
     217 
     218    def __setstate__(self, state): 
     219        self.baz = state 
     220 
     221class MyInt(int): 
     222 
     223    def __eq__(self, other): 
     224        return type(self) is type(other) and int(self) == int(other) 
     225 
     226class MyList(list): 
     227 
     228    def __init__(self, n=1): 
     229        self.extend([None]*n) 
     230 
     231    def __eq__(self, other): 
     232        return type(self) is type(other) and list(self) == list(other) 
     233 
     234class MyDict(dict): 
     235 
     236    def __init__(self, n=1): 
     237        for k in range(n): 
     238            self[k] = None 
     239 
     240    def __eq__(self, other): 
     241        return type(self) is type(other) and dict(self) == dict(other) 
    115242 
    116243class TestConstructorTypes(test_appliance.TestAppliance): 
  • pyyaml/trunk/tests/test_representer.py

    r144 r147  
    2525                    data2.sort() 
    2626                    data2 = repr(data2) 
    27                 if data1 != data2: 
     27                    if data1 != data2: 
     28                        raise 
     29                elif isinstance(data1, list): 
     30                    self.failUnlessEqual(type(data1), type(data2)) 
     31                    self.failUnlessEqual(len(data1), len(data2)) 
     32                    for item1, item2 in zip(data1, data2): 
     33                        self.failUnlessEqual(item1, item2) 
     34                else: 
    2835                    raise 
    2936        except: 
Note: See TracChangeset for help on using the changeset viewer.