Changeset 147 for pyyaml/trunk/lib/yaml

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

Add support for pickling/unpickling python objects.

Location:
pyyaml/trunk/lib/yaml
Files:
3 modified

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