Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/lib/syck/loaders.py

    r18 r25  
     1""" 
     2syck.loaders is a high-level wrapper for the Syck YAML parser. 
     3Do not use it directly, use the module 'syck' instead. 
     4""" 
    15 
    26# Python 2.2 compatibility 
     
    2125import _syck 
    2226 
    23 import re 
     27import sys, re 
    2428 
    2529__all__ = ['GenericLoader', 'Loader', 
     
    2731 
    2832class GenericLoader(_syck.Parser): 
     33    """ 
     34    GenericLoader constructs primitive Python objects from YAML documents. 
     35    """ 
    2936 
    3037    def load(self): 
     38        """ 
     39        Loads a YAML document from the source and return a native Python 
     40        object. On EOF, returns None and set the eof attribute on. 
     41        """ 
    3142        node = self.parse() 
    3243        if self.eof: 
     
    5061                value_object = self._convert(node.value[key_node], 
    5162                        node_to_object) 
    52                 if key_object in value: 
    53                     value = None 
    54                     break 
    5563                try: 
     64                    if key_object in value: 
     65                        value = None 
     66                        break 
    5667                    value[key_object] = value_object 
    5768                except TypeError: 
     
    6172                value = [] 
    6273                for key_node in node.value: 
    63                     key_object = self_convert(key_node, node_to_object) 
     74                    key_object = self._convert(key_node, node_to_object) 
    6475                    value_object = self._convert(node.value[key_node], 
    6576                            node_to_object) 
     
    7182 
    7283    def construct(self, node): 
     84        """Constructs a Python object by the given node.""" 
    7385        return node.value 
    7486 
    7587class Merge: 
     88    """Represents the merge key '<<'.""" 
    7689    pass 
    7790 
    7891class Default: 
     92    """Represents the default key '='.""" 
    7993    pass 
    8094 
    8195class Loader(GenericLoader): 
     96    """ 
     97    Loader constructs native Python objects from YAML documents. 
     98    """ 
    8299 
    83100    inf_value = 1e300000 
    84101    nan_value = inf_value/inf_value 
    85102 
    86     ymd_expr = re.compile(r'(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d)') 
    87103    timestamp_expr = re.compile(r'(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d)' 
    88104            r'(?:' 
     
    95111    default_key = Default() 
    96112 
    97     def __init__(self, *args, **kwds): 
    98         super(Loader, self).__init__(*args, **kwds) 
    99         self.tags = {} 
    100         self.add_builtin_types() 
    101  
    102     def add_builtin_types(self): 
    103         self.add_builtin_type('null', lambda node: None) 
    104         self.add_builtin_type('bool#yes', lambda node: True) 
    105         self.add_builtin_type('bool#no', lambda node: False) 
    106         self.add_builtin_type('float#fix', lambda node: float(node.value)) 
    107         self.add_builtin_type('float#exp', lambda node: float(node.value)) 
    108         self.add_builtin_type('float#base60', 'construct_base60_float') 
    109         self.add_builtin_type('float#inf', lambda node: self.inf_value) 
    110         self.add_builtin_type('float#neginf', lambda node: -self.inf_value) 
    111         self.add_builtin_type('float#nan', lambda node: self.nan_value) 
    112         self.add_builtin_type('int', lambda node: int(node.value)) 
    113         self.add_builtin_type('int#hex', lambda node: int(node.value, 16)) 
    114         self.add_builtin_type('int#oct', lambda node: int(node.value, 8)) 
    115         self.add_builtin_type('int#base60', 'construct_base60_int') 
    116         self.add_builtin_type('binary', lambda node: node.value.decode('base64')) 
    117         self.add_builtin_type('timestamp#ymd', 'construct_timestamp') 
    118         self.add_builtin_type('timestamp#iso8601', 'construct_timestamp') 
    119         self.add_builtin_type('timestamp#spaced', 'construct_timestamp') 
    120         self.add_builtin_type('timestamp', 'construct_timestamp') 
    121         self.add_builtin_type('merge', 'construct_merge') 
    122         self.add_builtin_type('default', 'construct_default') 
    123         self.add_builtin_type('omap', 'construct_omap') 
    124         self.add_builtin_type('pairs', 'construct_pairs') 
    125         self.add_builtin_type('set', 'construct_set') 
    126  
    127     def add_type(self, type_tag, constuctor): 
    128         self.tags[type_tag] = constructor 
    129  
    130     def add_domain_type(self, domain, type_tag, constructor): 
    131         self.tags['tag:%s:%s' % (domain, type_tag)] = constructor 
    132  
    133     def add_builtin_type(self, type_tag, constructor): 
    134         self.tags['tag:yaml.org,2002:'+type_tag] = constructor 
    135  
    136     def add_python_type(self, type_tag, constructor): 
    137         self.tags['tag:python.yaml.org,2002:'+type_tag] = constructor 
    138  
    139     def add_private_type(self, type_tag, constructor): 
    140         self.tags['x-private:'+type_tag] = constructor 
     113    non_ascii = [] 
     114    for i in range(256): 
     115        ch = chr(i) 
     116        if ch.isalnum(): 
     117            non_ascii.append(ch) 
     118        else: 
     119            non_ascii.append('_') 
     120    non_ascii = ''.join(non_ascii) 
     121 
     122    python_bools = {'True': True, 'False': False} 
     123 
     124    class python_class: 
     125        pass 
     126 
     127    def find_constructor(self, node): 
     128        """ 
     129        Returns the contructor for generating a Python object for the given 
     130        node. 
     131 
     132        The node tags are mapped to constructors by the following rule: 
     133 
     134        Tag                             Constructor 
     135        ---                             ----------- 
     136        tag:yaml.org,2002:type          construct_type 
     137        tag:python.yaml.org,2002:type   construct_python_type 
     138        x-private:type                  construct_private_type 
     139        tag:domain.tld,2002:type        construct_domain_tld_2002_type 
     140 
     141        See the method code for more details. 
     142        """ 
     143        parts = [] 
     144        if node.tag: 
     145            parts = node.tag.split(':') 
     146        if parts: 
     147            if parts[0] == 'tag': 
     148                parts.pop(0) 
     149                if parts: 
     150                    if parts[0] == 'yaml.org,2002': 
     151                        parts.pop(0) 
     152                    elif parts[0] == 'python.yaml.org,2002': 
     153                        parts[0] = 'python' 
     154            elif parts[0] == 'x-private': 
     155                parts[0] = 'private' 
     156        parts = [part.translate(self.non_ascii) for part in parts] 
     157        while parts: 
     158            method = 'construct_'+'_'.join(parts) 
     159            if hasattr(self, method): 
     160                return getattr(self, method) 
     161            parts.pop() 
    141162 
    142163    def construct(self, node): 
     164        """Constructs a Python object by the given node.""" 
    143165        if node.kind == 'map' and self.merge_key in node.value: 
    144166            self.merge_maps(node) 
    145         if node.tag in self.tags: 
    146             constructor = self.tags[node.tag] 
    147             if isinstance(constructor, str): 
    148                 constructor = getattr(self, constructor) 
     167        constructor = self.find_constructor(node) 
     168        if constructor: 
    149169            return constructor(node) 
    150170        else: 
    151171            return node.value 
    152172 
    153     def construct_base60_float(self, node): 
    154         return self.construct_base60(float, node) 
    155  
    156     def construct_base60_int(self, node): 
    157         return self.construct_base60(int, node) 
    158  
    159     def construct_base60(self, num_type, node): 
     173    def construct_null(self, node): 
     174        return None 
     175 
     176    def construct_bool_yes(self, node): 
     177        return True 
     178 
     179    def construct_bool_no(self, node): 
     180        return False 
     181 
     182    def construct_numeric_base60(self, num_type, node): 
    160183        digits = [num_type(part) for part in node.value.split(':')] 
    161184        digits.reverse() 
     
    166189            base *= 60 
    167190        return value 
     191 
     192    def construct_int(self, node): 
     193        return int(node.value) 
     194 
     195    def construct_int_hex(self, node): 
     196        return int(node.value, 16) 
     197 
     198    def construct_int_oct(self, node): 
     199        return int(node.value, 8) 
     200 
     201    def construct_int_base60(self, node): 
     202        return self.construct_numeric_base60(int, node) 
     203 
     204    def construct_float(self, node): 
     205        return float(node.value) 
     206    construct_float_fix = construct_float 
     207    construct_float_exp = construct_float 
     208 
     209    def construct_float_base60(self, node): 
     210        return self.construct_numeric_base60(float, node) 
     211 
     212    def construct_float_inf(self, node): 
     213        return self.inf_value 
     214 
     215    def construct_float_neginf(self, node): 
     216        return -self.inf_value 
     217 
     218    def construct_float_nan(self, node): 
     219        return self.nan_value 
     220 
     221    def construct_binary(self, node): 
     222        return node.value.decode('base64') 
    168223 
    169224    def construct_timestamp(self, node): 
     
    183238        diff = datetime.timedelta(hours=values['zhour'], minutes=values['zminute']) 
    184239        return stamp-diff 
     240    construct_timestamp_ymd = construct_timestamp 
     241    construct_timestamp_iso8601 = construct_timestamp 
     242    construct_timestamp_spaced = construct_timestamp 
    185243 
    186244    def construct_merge(self, node): 
     
    217275        return sets.Set(node.value) 
    218276 
    219 def parse(source): 
     277    def construct_python_none(self, node): 
     278        return None 
     279 
     280    def construct_python_bool(self, node): 
     281        return self.python_bools[node.value] 
     282 
     283    def construct_python_int(self, node): 
     284        return int(node.value) 
     285 
     286    def construct_python_long(self, node): 
     287        return long(node.value) 
     288 
     289    def construct_python_float(self, node): 
     290        return float(node.value) 
     291 
     292    def construct_python_str(self, node): 
     293        return str(node.value) 
     294 
     295    def construct_python_unicode(self, node): 
     296        return unicode(node.value, 'utf-8') 
     297 
     298    def construct_python_list(self, node): 
     299        return node.value 
     300 
     301    def construct_python_tuple(self, node): 
     302        return tuple(node.value) 
     303 
     304    def construct_python_dict(self, node): 
     305        return node.value 
     306 
     307    def find_python_object(self, node): 
     308        full_name = node.tag.split(':')[3] 
     309        parts = full_name.split('.') 
     310        object_name = parts.pop() 
     311        module_name = '.'.join(parts) 
     312        if not module_name: 
     313            module_name = '__builtin__' 
     314        else: 
     315            __import__(module_name) 
     316        return getattr(sys.modules[module_name], object_name) 
     317 
     318    def find_python_state(self, node): 
     319        if node.kind == 'seq': 
     320            args = node.value 
     321            kwds = {} 
     322            state = {} 
     323        else: 
     324            args = node.value.get('args', []) 
     325            kwds = node.value.get('kwds', {}) 
     326            state = node.value.get('state', {}) 
     327        return args, kwds, state 
     328 
     329    def set_python_state(self, object, state): 
     330        if hasattr(object, '__setstate__'): 
     331            object.__setstate__(state) 
     332        else: 
     333            slotstate = {} 
     334            if isinstance(state, tuple) and len(state) == 2: 
     335                state, slotstate = state 
     336            if hasattr(object, '__dict__'): 
     337                object.__dict__.update(state) 
     338            elif state: 
     339                slotstate.update(state) 
     340            for key, value in slotstate.items(): 
     341                setattr(object, key, value) 
     342 
     343    def construct_python_name(self, node): 
     344        return self.find_python_object(node) 
     345 
     346    def construct_python_object(self, node): 
     347        cls = self.find_python_object(node) 
     348        if type(cls) is type(self.python_class): 
     349            if hasattr(cls, '__getnewargs__'): 
     350                object = cls() 
     351            else: 
     352                object = self.python_class() 
     353                object.__class__ = cls 
     354        else: 
     355            object = cls.__new__(cls) 
     356        self.set_python_state(object, node.value) 
     357        return object 
     358 
     359    def construct_python_new(self, node): 
     360        cls = self.find_python_object(node) 
     361        args, kwds, state = self.find_python_state(node) 
     362        if type(cls) is type(self.python_class): 
     363            object = cls(*args, **kwds) 
     364        else: 
     365            object = cls.__new__(cls, *args, **kwds) 
     366        self.set_python_state(object, state) 
     367        return object 
     368 
     369    def construct_python_apply(self, node): 
     370        constructor = self.find_python_object(node) 
     371        args, kwds, state = self.find_python_state(node) 
     372        object = constructor(*args, **kwds) 
     373        self.set_python_state(object, state) 
     374        return object 
     375 
     376def parse(source, Loader=Loader, **parameters): 
    220377    """Parses 'source' and returns the root of the 'Node' graph.""" 
    221     loader = Loader(source) 
     378    loader = Loader(source, **parameters) 
    222379    return loader.parse() 
    223380 
    224 def load(source): 
     381def load(source, Loader=Loader, **parameters): 
    225382    """Parses 'source' and returns the root object.""" 
    226     loader = Loader(source) 
     383    loader = Loader(source, **parameters) 
    227384    return loader.load() 
    228385 
    229 def parse_documents(source): 
    230     """Iterates over 'source' and yields the root node of each document.""" 
    231     loader = Loader(source) 
     386def parse_documents(source, Loader=Loader, **parameters): 
     387    """Iterates over 'source' and yields the root 'Node' for each document.""" 
     388    loader = Loader(source, **parameters) 
    232389    while True: 
    233390        node = loader.parse() 
     
    236393        yield node 
    237394 
    238 def load_documents(source): 
    239     """Iterates over 'source' and yields the root object of each document.""" 
    240     loader = Loader(source) 
     395def load_documents(source, Loader=Loader, **parameters): 
     396    """Iterates over 'source' and yields the root object for each document.""" 
     397    loader = Loader(source, **parameters) 
    241398    while True: 
    242399        object = loader.load() 
Note: See TracChangeset for help on using the changeset viewer.