Ignore:
Timestamp:
08/03/06 12:07:29 (8 years ago)
Author:
xi
Message:

Subclass all base classes from object.

Hold references to the objects being represented (should fix #22).

The value of a mapping node is represented as a list of pairs (key, value)
now.

Sort dictionary items (fix #23).

Recursive structures are now loaded and dumped correctly, including complex
structures like recursive tuples (fix #5). Thanks Peter Murphy for the patches.
To make it possible, representer functions are allowed to be generators.
In this case, the first generated value is an object. Other values produced
by the representer are ignored.

Make Representer not try to guess !!pairs when a list is represented.
You need to construct a !!pairs node explicitly now.

Do not check for duplicate mapping keys as it didn't work correctly anyway.

File:
1 edited

Legend:

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

    r198 r222  
    2222    pass 
    2323 
    24 class BaseConstructor: 
     24class BaseConstructor(object): 
    2525 
    2626    yaml_constructors = {} 
     
    3030        self.constructed_objects = {} 
    3131        self.recursive_objects = {} 
     32        self.state_generators = [] 
     33        self.deep_construct = False 
    3234 
    3335    def check_data(self): 
     
    4042            return self.construct_document(self.get_node()) 
    4143 
     44    def g(): yield None 
     45    generator_type = type(g()) 
     46    del g 
     47 
    4248    def construct_document(self, node): 
    4349        data = self.construct_object(node) 
     50        while self.state_generators: 
     51            state_generators = self.state_generators 
     52            self.state_generators = [] 
     53            for generator in state_generators: 
     54                for dummy in generator: 
     55                    pass 
    4456        self.constructed_objects = {} 
    4557        self.recursive_objects = {} 
     58        self.deep_construct = False 
    4659        return data 
    4760 
    48     def construct_object(self, node): 
     61    def construct_object(self, node, deep=False): 
     62        if deep: 
     63            old_deep = self.deep_construct 
     64            self.deep_construct = True 
    4965        if node in self.constructed_objects: 
    5066            return self.constructed_objects[node] 
    5167        if node in self.recursive_objects: 
    5268            raise ConstructorError(None, None, 
    53                     "found recursive node", node.start_mark) 
     69                    "found unconstructable recursive node", node.start_mark) 
    5470        self.recursive_objects[node] = None 
    5571        constructor = None 
     72        state_constructor = None 
     73        tag_suffix = None 
    5674        if node.tag in self.yaml_constructors: 
    57             constructor = lambda node: self.yaml_constructors[node.tag](self, node) 
     75            constructor = self.yaml_constructors[node.tag] 
    5876        else: 
    5977            for tag_prefix in self.yaml_multi_constructors: 
    6078                if node.tag.startswith(tag_prefix): 
    6179                    tag_suffix = node.tag[len(tag_prefix):] 
    62                     constructor = lambda node:  \ 
    63                             self.yaml_multi_constructors[tag_prefix](self, tag_suffix, node) 
     80                    constructor = self.yaml_multi_constructors[tag_prefix] 
    6481                    break 
    6582            else: 
    6683                if None in self.yaml_multi_constructors: 
    67                     constructor = lambda node:  \ 
    68                             self.yaml_multi_constructors[None](self, node.tag, node) 
     84                    tag_suffix = node.tag 
     85                    constructor = self.yaml_multi_constructors[None] 
    6986                elif None in self.yaml_constructors: 
    70                     constructor = lambda node:  \ 
    71                             self.yaml_constructors[None](self, node) 
     87                    constructor = self.yaml_constructors[None] 
    7288                elif isinstance(node, ScalarNode): 
    73                     constructor = self.construct_scalar 
     89                    constructor = self.__class__.construct_scalar 
    7490                elif isinstance(node, SequenceNode): 
    75                     constructor = self.construct_sequence 
     91                    constructor = self.__class__.construct_sequence 
    7692                elif isinstance(node, MappingNode): 
    77                     constructor = self.construct_mapping 
    78                 else: 
    79                     print node.tag 
    80         data = constructor(node) 
     93                    constructor = self.__class__.construct_mapping 
     94        if tag_suffix is None: 
     95            data = constructor(self, node) 
     96        else: 
     97            data = constructor(self, tag_suffix, node) 
     98        if isinstance(data, self.generator_type): 
     99            generator = data 
     100            data = generator.next() 
     101            if self.deep_construct: 
     102                for dummy in generator: 
     103                    pass 
     104            else: 
     105                self.state_generators.append(generator) 
    81106        self.constructed_objects[node] = data 
    82107        del self.recursive_objects[node] 
     108        if deep: 
     109            self.deep_construct = old_deep 
    83110        return data 
    84111 
    85112    def construct_scalar(self, node): 
    86113        if not isinstance(node, ScalarNode): 
    87             if isinstance(node, MappingNode): 
    88                 for key_node in node.value: 
    89                     if key_node.tag == u'tag:yaml.org,2002:value': 
    90                         return self.construct_scalar(node.value[key_node]) 
    91114            raise ConstructorError(None, None, 
    92115                    "expected a scalar node, but found %s" % node.id, 
     
    94117        return node.value 
    95118 
    96     def construct_sequence(self, node): 
     119    def construct_sequence(self, node, deep=False): 
    97120        if not isinstance(node, SequenceNode): 
    98121            raise ConstructorError(None, None, 
    99122                    "expected a sequence node, but found %s" % node.id, 
    100123                    node.start_mark) 
    101         return [self.construct_object(child) for child in node.value] 
    102  
    103     def construct_mapping(self, node): 
     124        return [self.construct_object(child, deep=deep) 
     125                for child in node.value] 
     126 
     127    def construct_mapping(self, node, deep=False): 
    104128        if not isinstance(node, MappingNode): 
    105129            raise ConstructorError(None, None, 
     
    107131                    node.start_mark) 
    108132        mapping = {} 
    109         merge = None 
    110         for key_node in node.value: 
     133        for key_node, value_node in node.value: 
     134            key = self.construct_object(key_node, deep=deep) 
     135            try: 
     136                hash(key) 
     137            except TypeError, exc: 
     138                raise ConstructorError("while constructing a mapping", node.start_mark, 
     139                        "found unacceptable key (%s)" % exc, key_node.start_mark) 
     140            value = self.construct_object(value_node, deep=deep) 
     141            mapping[key] = value 
     142        return mapping 
     143 
     144    def construct_pairs(self, node, deep=False): 
     145        if not isinstance(node, MappingNode): 
     146            raise ConstructorError(None, None, 
     147                    "expected a mapping node, but found %s" % node.id, 
     148                    node.start_mark) 
     149        pairs = [] 
     150        for key_node, value_node in node.value: 
     151            key = self.construct_object(key_node, deep=deep) 
     152            value = self.construct_object(value_node, deep=deep) 
     153            pairs.append((key, value)) 
     154        return pairs 
     155 
     156    def add_constructor(cls, tag, constructor): 
     157        if not 'yaml_constructors' in cls.__dict__: 
     158            cls.yaml_constructors = cls.yaml_constructors.copy() 
     159        cls.yaml_constructors[tag] = constructor 
     160    add_constructor = classmethod(add_constructor) 
     161 
     162    def add_multi_constructor(cls, tag_prefix, multi_constructor): 
     163        if not 'yaml_multi_constructors' in cls.__dict__: 
     164            cls.yaml_multi_constructors = cls.yaml_multi_constructors.copy() 
     165        cls.yaml_multi_constructors[tag_prefix] = multi_constructor 
     166    add_multi_constructor = classmethod(add_multi_constructor) 
     167 
     168class SafeConstructor(BaseConstructor): 
     169 
     170    def construct_scalar(self, node): 
     171        if isinstance(node, MappingNode): 
     172            for key_node, value_node in node.value: 
     173                if key_node.tag == u'tag:yaml.org,2002:value': 
     174                    return self.construct_scalar(value_node) 
     175        return BaseConstructor.construct_scalar(self, node) 
     176 
     177    def flatten_mapping(self, node): 
     178        merge = [] 
     179        index = 0 
     180        while index < len(node.value): 
     181            key_node, value_node = node.value[index] 
    111182            if key_node.tag == u'tag:yaml.org,2002:merge': 
    112                 if merge is not None: 
    113                     raise ConstructorError("while constructing a mapping", node.start_mark, 
    114                             "found duplicate merge key", key_node.start_mark) 
    115                 value_node = node.value[key_node] 
     183                del node.value[index] 
    116184                if isinstance(value_node, MappingNode): 
    117                     merge = [self.construct_mapping(value_node)] 
     185                    self.flatten_mapping(value_node) 
     186                    merge.extend(value_node.value) 
    118187                elif isinstance(value_node, SequenceNode): 
    119                     merge = [] 
     188                    submerge = [] 
    120189                    for subnode in value_node.value: 
    121190                        if not isinstance(subnode, MappingNode): 
     
    124193                                    "expected a mapping for merging, but found %s" 
    125194                                    % subnode.id, subnode.start_mark) 
    126                         merge.append(self.construct_mapping(subnode)) 
    127                     merge.reverse() 
     195                        self.flatten_mapping(subnode) 
     196                        submerge.append(subnode.value) 
     197                    submerge.reverse() 
     198                    for value in submerge: 
     199                        merge.extend(value) 
    128200                else: 
    129201                    raise ConstructorError("while constructing a mapping", node.start_mark, 
     
    131203                            % value_node.id, value_node.start_mark) 
    132204            elif key_node.tag == u'tag:yaml.org,2002:value': 
    133                 if '=' in mapping: 
    134                     raise ConstructorError("while construction a mapping", node.start_mark, 
    135                             "found duplicate value key", key_node.start_mark) 
    136                 value = self.construct_object(node.value[key_node]) 
    137                 mapping['='] = value 
     205                key_node.tag = u'tag:yaml.org,2002:str' 
     206                index += 1 
    138207            else: 
    139                 key = self.construct_object(key_node) 
    140                 try: 
    141                     duplicate_key = key in mapping 
    142                 except TypeError, exc: 
    143                     raise ConstructorError("while constructing a mapping", node.start_mark, 
    144                             "found unacceptable key (%s)" % exc, key_node.start_mark) 
    145                 if duplicate_key: 
    146                     raise ConstructorError("while constructing a mapping", node.start_mark, 
    147                             "found duplicate key", key_node.start_mark) 
    148                 value = self.construct_object(node.value[key_node]) 
    149                 mapping[key] = value 
    150         if merge is not None: 
    151             merge.append(mapping) 
    152             mapping = {} 
    153             for submapping in merge: 
    154                 mapping.update(submapping) 
    155         return mapping 
    156  
    157     def construct_pairs(self, node): 
    158         if not isinstance(node, MappingNode): 
    159             raise ConstructorError(None, None, 
    160                     "expected a mapping node, but found %s" % node.id, 
    161                     node.start_mark) 
    162         pairs = [] 
    163         for key_node in node.value: 
    164             key = self.construct_object(key_node) 
    165             value = self.construct_object(node.value[key_node]) 
    166             pairs.append((key, value)) 
    167         return pairs 
    168  
    169     def add_constructor(cls, tag, constructor): 
    170         if not 'yaml_constructors' in cls.__dict__: 
    171             cls.yaml_constructors = cls.yaml_constructors.copy() 
    172         cls.yaml_constructors[tag] = constructor 
    173     add_constructor = classmethod(add_constructor) 
    174  
    175     def add_multi_constructor(cls, tag_prefix, multi_constructor): 
    176         if not 'yaml_multi_constructors' in cls.__dict__: 
    177             cls.yaml_multi_constructors = cls.yaml_multi_constructors.copy() 
    178         cls.yaml_multi_constructors[tag_prefix] = multi_constructor 
    179     add_multi_constructor = classmethod(add_multi_constructor) 
    180  
    181 class SafeConstructor(BaseConstructor): 
     208                index += 1 
     209        if merge: 
     210            node.value = merge + node.value 
     211 
     212    def construct_mapping(self, node, deep=False): 
     213        if isinstance(node, MappingNode): 
     214            self.flatten_mapping(node) 
     215        return BaseConstructor.construct_mapping(self, node, deep=deep) 
    182216 
    183217    def construct_yaml_null(self, node): 
     
    297331        # Note: we do not check for duplicate keys, because it's too 
    298332        # CPU-expensive. 
     333        omap = [] 
     334        yield omap 
    299335        if not isinstance(node, SequenceNode): 
    300336            raise ConstructorError("while constructing an ordered map", node.start_mark, 
    301337                    "expected a sequence, but found %s" % node.id, node.start_mark) 
    302         omap = [] 
    303338        for subnode in node.value: 
    304339            if not isinstance(subnode, MappingNode): 
     
    310345                        "expected a single mapping item, but found %d items" % len(subnode.value), 
    311346                        subnode.start_mark) 
    312             key_node = subnode.value.keys()[0] 
     347            key_node, value_node = subnode.value[0] 
    313348            key = self.construct_object(key_node) 
    314             value = self.construct_object(subnode.value[key_node]) 
     349            value = self.construct_object(value_node) 
    315350            omap.append((key, value)) 
    316         return omap 
    317351 
    318352    def construct_yaml_pairs(self, node): 
    319353        # Note: the same code as `construct_yaml_omap`. 
     354        pairs = [] 
     355        yield pairs 
    320356        if not isinstance(node, SequenceNode): 
    321357            raise ConstructorError("while constructing pairs", node.start_mark, 
    322358                    "expected a sequence, but found %s" % node.id, node.start_mark) 
    323         pairs = [] 
    324359        for subnode in node.value: 
    325360            if not isinstance(subnode, MappingNode): 
     
    331366                        "expected a single mapping item, but found %d items" % len(subnode.value), 
    332367                        subnode.start_mark) 
    333             key_node = subnode.value.keys()[0] 
     368            key_node, value_node = subnode.value[0] 
    334369            key = self.construct_object(key_node) 
    335             value = self.construct_object(subnode.value[key_node]) 
     370            value = self.construct_object(value_node) 
    336371            pairs.append((key, value)) 
    337         return pairs 
    338372 
    339373    def construct_yaml_set(self, node): 
     374        data = set() 
     375        yield data 
    340376        value = self.construct_mapping(node) 
    341         return set(value) 
     377        data.update(value) 
    342378 
    343379    def construct_yaml_str(self, node): 
     
    349385 
    350386    def construct_yaml_seq(self, node): 
    351         return self.construct_sequence(node) 
     387        data = [] 
     388        yield data 
     389        data.extend(self.construct_sequence(node)) 
    352390 
    353391    def construct_yaml_map(self, node): 
    354         return self.construct_mapping(node) 
     392        data = {} 
     393        yield data 
     394        value = self.construct_mapping(node) 
     395        data.update(value) 
    355396 
    356397    def construct_yaml_object(self, node, cls): 
    357         state = self.construct_mapping(node) 
    358398        data = cls.__new__(cls) 
     399        yield data 
    359400        if hasattr(data, '__setstate__'): 
     401            state = self.construct_mapping(node, deep=True) 
    360402            data.__setstate__(state) 
    361403        else: 
     404            state = self.construct_mapping(node) 
    362405            data.__dict__.update(state) 
    363         return data 
    364406 
    365407    def construct_undefined(self, node): 
     
    435477 
    436478    def construct_python_tuple(self, node): 
    437         return tuple(self.construct_yaml_seq(node)) 
     479        return tuple(self.construct_sequence(node)) 
    438480 
    439481    def find_python_module(self, name, mark): 
     
    526568        #   !!python/object:module.name { ... state ... } 
    527569        instance = self.make_python_instance(suffix, node, newobj=True) 
    528         state = self.construct_mapping(node) 
     570        yield instance 
     571        deep = hasattr(instance, '__setstate__') 
     572        state = self.construct_mapping(node, deep=deep) 
    529573        self.set_python_instance_state(instance, state) 
    530         return instance 
    531574 
    532575    def construct_python_object_apply(self, suffix, node, newobj=False): 
     
    543586        # is how an object is created, check make_python_instance for details. 
    544587        if isinstance(node, SequenceNode): 
    545             args = self.construct_sequence(node) 
     588            args = self.construct_sequence(node, deep=True) 
    546589            kwds = {} 
    547590            state = {} 
     
    549592            dictitems = {} 
    550593        else: 
    551             value = self.construct_mapping(node) 
     594            value = self.construct_mapping(node, deep=True) 
    552595            args = value.get('args', []) 
    553596            kwds = value.get('kwds', {}) 
     
    568611        return self.construct_python_object_apply(suffix, node, newobj=True) 
    569612 
    570  
    571613Constructor.add_constructor( 
    572614    u'tag:yaml.org,2002:python/none', 
Note: See TracChangeset for help on using the changeset viewer.