Changeset 557

Show
Ignore:
Timestamp:
07/10/08 10:55:04 (5 months ago)
Author:
athomas
Message:
  • Completely delegate attribute casting to the Node.
  • Add doctests and unit tests.
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • cly/trunk/cly/builder.py

    r556 r557  
    479479 
    480480    @classmethod 
    481     def xml_attribute_casts(cls): 
     481    def xml_cast_attribute(cls, name, value): 
    482482        """Define functions for casting attributes to their correct Python type. 
    483483 
    484         Parent classes are automatically merged, so upcalling is unnecessary. 
    485  
    486         :returns: A dictionary of name, function mappings
    487         """ 
    488         return
     484        Upcalling is necessary. 
     485 
     486        :returns: Cast value
     487        """ 
     488        casts =
    489489            'traversals': int, 'group': _xml_group_type, 
    490490            'order': int, 'match_candidates': _xml_boolean_type, 
    491491            'with_context': _xml_boolean_type, 
    492492            } 
     493        if name in casts: 
     494            value = casts[name](value) 
     495        return value 
    493496 
    494497    @classmethod 
     
    564567 
    565568class Set(Masquerade): 
    566     """Set variables in a branch.""" 
    567     def __init__(self, vars='', unset='', **kwargs): 
    568         self.vars = eval('dict(%s)' % vars) 
    569         self.unset = unset 
    570         super(Set, self).__init__(**kwargs) 
     569    """Set variables in a branch. 
     570 
     571    >>> from cly import * 
     572    >>> parser = Parser(Grammar(Set(foo=10, bar=20)(baz=Node()))) 
     573    >>> parser.parse('baz').vars 
     574    {'foo': 10, 'bar': 20} 
     575    """ 
     576    vars = '' 
     577    unset = '' 
     578 
     579    def __init__(self, **kwargs): 
     580        super(Set, self).__init__() 
     581        self.vars = kwargs 
    571582 
    572583    def follow(self, context): 
     
    585596 
    586597    @classmethod 
    587     def xml_attribute_aliases(cls): 
    588         return {'id': _xml_group_type} 
     598    def xml_cast_attribute(cls, name, value): 
     599        if name == 'id': 
     600            return _xml_group_type(value) 
     601        return super(Group, cls).xml_cast_attribute(name, value) 
    589602 
    590603 
     
    10261039        # callbacks. 
    10271040        aliases = {} 
    1028         attr_type_map = {} 
    1029         def call_and_merge(method_name, d): 
    1030             for c in reversed(cls.mro()): 
    1031                 if method_name in c.__dict__: 
    1032                     d.update(getattr(c, method_name)()) 
    1033         call_and_merge('xml_attribute_aliases', aliases) 
    1034         call_and_merge('xml_attribute_casts', attr_type_map) 
     1041        for c in reversed(cls.mro()): 
     1042            if 'xml_attribute_aliases' in c.__dict__: 
     1043                aliases.update(c.xml_attribute_aliases()) 
    10351044 
    10361045        attributes = dict([(str(aliases.get(k, k)), v) for k, v 
     
    10381047 
    10391048        for k, v in attributes.items(): 
    1040             # Do type-map conversion 
    1041             if k in attr_type_map: 
    1042                 v = attr_type_map[k](v) 
     1049            # Do type conversion 
     1050            v = cls.xml_cast_attribute(k, v) 
    10431051            # Is the destination Node attribute callable? Do a lazy eval. 
    1044             elif callable(getattr(cls, k, None)): 
     1052            if callable(getattr(cls, k, None)): 
    10451053                function = getattr(cls, k) 
    10461054                if callable(function): 
     
    13791387        :suffix: Optional domain suffix to require. 
    13801388 
    1381     >>> from cly.parser import Parser 
     1389    >>> from cly.parser import * 
     1390 
     1391    Supports bare hostnames: 
     1392 
     1393    >>> parser = Parser(Grammar(foo=Hostname())) 
     1394    >>> parser.parse('www').vars 
     1395    {'foo': 'www'} 
     1396 
     1397    Fully-qualified names: 
     1398 
     1399    >>> parser.parse('www.example.com').vars 
     1400    {'foo': 'www.example.com'} 
     1401 
     1402    IN-ADDR ARPA addresses: 
     1403 
     1404    >>> parser.parse('1.1.10.in-addr.arpa').vars 
     1405    {'foo': '1.1.10.in-addr.arpa'} 
     1406 
     1407    But not IP addresses: 
     1408 
     1409    >>> parser.parse('10.1.1.1').vars 
     1410    {} 
     1411 
     1412    Suffix checking is also supported: 
     1413 
     1414    >>> parser = Parser(Grammar(foo=Hostname(suffix='.example.com'))) 
     1415    >>> parser.parse('www').vars 
     1416    {} 
     1417    >>> parser.parse('www.example.com').vars 
     1418    {'foo': 'www.example.com'} 
     1419 
     1420    As well as requiring a minumum number of host parts: 
     1421 
    13821422    >>> parser = Parser(Grammar(foo=Hostname(parts=2))) 
    1383     >>> parser.parse('www').vars['foo'] 
    1384     Traceback (most recent call last): 
    1385     ... 
    1386     KeyError: 'foo' 
    1387     >>> parser.parse('www.example.com').vars['foo'] 
    1388     'www.example.com' 
    1389     >>> parser.parse('1.1.10.in-addr.arpa').vars['foo'] 
    1390     '1.1.10.in-addr.arpa' 
    1391     >>> parser.parse('10.1.1.1').vars['foo'] 
    1392     Traceback (most recent call last): 
    1393     ... 
    1394     KeyError: 'foo' 
    1395  
     1423    >>> parser.parse('www').vars 
     1424    {} 
     1425    >>> parser.parse('www.foo.com').vars 
     1426    {'foo': 'www.foo.com'} 
    13961427    """ 
    13971428    pattern = r'(?i)([A-Z0-9][A-Z0-9_-]*)((\.([A-Z0-9][A-Z0-9_-]*))*\.([A-Z0-9][A-Z0-9_-]*[A-Z]))?\.?' 
     
    14071438            match = None 
    14081439        return match 
     1440 
     1441    @classmethod 
     1442    def xml_cast_attribute(cls): 
     1443        return {'parts': _xml_boolean_type} 
    14091444 
    14101445 
  • cly/trunk/cly/interactive.py

    r556 r557  
    133133            print >> sys.stderr, \ 
    134134                'WARNING: neither pyreadline nor CLY\'s built-in readline ' \ 
    135                 'extensions found,\n         contextual help are not ' \ 
     135                'extensions found,\n         contextual help is not ' \ 
    136136                'available.' 
    137137        return readline 
  • cly/trunk/cly/test.py

    r556 r557  
    1111from StringIO import StringIO 
    1212from cly.exceptions import InvalidToken 
    13 from cly import XMLGrammar, Parser 
     13from cly import Node, XMLGrammar, Parser 
    1414 
    1515 
     
    159159        parser.execute('echo hello') 
    160160        self.assertEqual(self._output, (('hello',), {})) 
     161 
     162    def test_xml_cast_attribute(self): 
     163        class Test(Node): 
     164            @classmethod 
     165            def xml_cast_attribute(cls, name, value): 
     166                if name == 'test': 
     167                    return int(value) 
     168                return value 
     169 
     170        xml = StringIO("""<?xml version="1.0"?> 
     171        <grammar> 
     172            <test test="10" name="test"> 
     173            </test> 
     174        </grammar> 
     175        """) 
     176        grammar = XMLGrammar(xml, extra_nodes=[Test]) 
     177        self.assertTrue(isinstance(grammar.find('/test').test, int)) 
     178 
     179    def test_xml_attribute_aliases(self): 
     180        class Parent(Node): 
     181            @classmethod 
     182            def xml_attribute_aliases(cls): 
     183                return {'foo': 'bar'} 
     184 
     185        class Test(Parent): 
     186            @classmethod 
     187            def xml_attribute_aliases(cls): 
     188                return {'baz': 'waz'} 
     189 
     190        xml = StringIO("""<?xml version="1.0"?> 
     191        <grammar> 
     192            <test baz="10" foo="20" name="test"> 
     193            </test> 
     194        </grammar> 
     195        """) 
     196        grammar = XMLGrammar(xml, extra_nodes=[Test]) 
     197        node = grammar.find('/test') 
     198        self.assertTrue(node.bar, '10') 
     199        self.assertTrue(node.waz, '20') 
    161200 
    162201