Changeset 576

Show
Ignore:
Timestamp:
07/14/08 21:39:03 (3 months ago)
Author:
athomas
Message:

Delegated all attribute casting to cast_attribute(), including transformation
to callbacks and interpretation of EVAL_NS.

Files:

Legend:

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

    r575 r576  
    120120    traversals = 1 
    121121    label = None 
    122     context = None 
    123122 
    124123    def __init__(self, *anonymous, **kwargs): 
     
    127126        help = kwargs.pop('help', '') 
    128127        if isinstance(help, basestring): 
    129             self.help = LazyHelp(self, help) 
     128            self._help = help 
    130129        elif callable(help): 
    131130            self.help = help 
     
    177176                    lambda self, name: self._set_name(name)) 
    178177 
     178    def help(self, context): 
     179        """Return help for node. 
     180 
     181        :returns: A sequence of tuples in the form (lhs, help). 
     182        """ 
     183        if self.name == self.pattern: 
     184            yield (self.name, self._help) 
     185        else: 
     186            yield ('<%s>' % self.name, self._help) 
    179187 
    180188    def __call__(self, *anonymous, **options): 
     
    479487 
    480488    @classmethod 
    481     def cast_attribute(cls, name, value): 
     489    def cast_attribute(cls, namespace, name, value): 
    482490        """Define functions for casting attributes to their correct Python type. 
    483491 
    484492        Upcalling is necessary. 
    485493 
    486         :returns: Cast value. 
     494        :namespace: The XML namespace of the attribute. Compare against 
     495                    XMLGrammar.EVAL_NS to determine if forced evaluation 
     496                    can/should occur. 
     497        :name: Attribute name. 
     498        :value: Value to cast. 
     499 
     500        :returns: Tuple of (value, options) where options is a dictionary of 
     501                  extra Node constructor arguments. 
    487502        """ 
    488503        casts = { 
     
    492507            } 
    493508        if name in casts: 
    494             value = casts[name](value) 
    495         return value 
     509            return casts[name](value), {} 
     510 
     511        # Attributes that can be strings but by default have a method. 
     512        if name == 'help' and namespace != XMLGrammar.EVAL_NS: 
     513            return value, {} 
     514 
     515        # Is the destination Node attribute callable? Do a lazy eval. 
     516        function = getattr(cls, name, None) 
     517        if callable(function): 
     518            args, _, _, _ = getargspec(function) 
     519            if args and args[0] == 'self': 
     520                args.pop(0) 
     521            value = lazy_attr_evaluator(value, args) 
     522        return value, {} 
    496523 
    497524    @classmethod 
     
    591618 
    592619    @classmethod 
    593     def cast_attribute(cls, name, value): 
    594         return eval(value) 
     620    def cast_attribute(cls, namespace, name, value): 
     621        return eval(value), {} 
    595622 
    596623 
     
    605632 
    606633    @classmethod 
    607     def cast_attribute(cls, name, value): 
     634    def cast_attribute(cls, namespace, name, value): 
    608635        if name == 'id': 
    609             return group_cast(value) 
    610         return super(Group, cls).cast_attribute(name, value) 
     636            return group_cast(value), {} 
     637        return super(Group, cls).cast_attribute(namespace, name, value) 
    611638 
    612639 
     
    812839        return {'exec': 'callback'} 
    813840 
     841    @classmethod 
     842    def cast_attribute(cls, namespace, name, value): 
     843        options = {} 
     844        if name == 'callback': 
     845            args = ['context'] 
     846            options['with_context'] = True 
     847            value = lazy_attr_evaluator(value, args) 
     848            return value, options 
     849        return super(Action, cls).cast_attribute(namespace, name, value) 
     850 
    814851 
    815852class Variable(Node): 
     
    9861023    """ 
    9871024 
    988     node_aliases = { 
     1025    NODE_ALIASES = { 
    9891026        'var': 'variable', 
    9901027        'int': 'integer', 
    9911028        'str': 'string', 
    9921029        } 
     1030    EVAL_NS = 'http://swapoff.org/cly/xml/eval' 
    9931031 
    9941032    def __init__(self, file, extra_nodes=None): 
     
    10021040        self.node_map = dict([(v.__name__.lower(), v) for v in nodes]) 
    10031041        self.node_map.update([(k, self.node_map[v]) 
    1004                               for k, v in self.node_aliases.items()]) 
     1042                              for k, v in self.NODE_ALIASES.items()]) 
    10051043 
    10061044        grammar = dom.firstChild 
     
    10571095                aliases.update(c.attribute_aliases()) 
    10581096 
    1059         attributes = dict([(str(aliases.get(k, k)), v) for k, v 
    1060                            in xnode.attributes.items()]) 
    1061  
    1062         for k, v in attributes.items(): 
     1097        attributes = {} 
     1098 
     1099        for (ns, k), v in xnode.attributes.itemsNS(): 
    10631100            # Do type conversion 
    1064             v = cls.cast_attribute(k, v) 
    1065             # Is the destination Node attribute callable? Do a lazy eval. 
    1066             if callable(getattr(cls, k, None)): 
    1067                 function = getattr(cls, k) 
    1068                 if callable(function): 
    1069                     args, _, _, _ = getargspec(function) 
    1070                     if args and args[0] == 'self': 
    1071                         args.pop(0) 
    1072                     # Special-case callback(*args, **kwargs) 
    1073                     if issubclass(cls, Action) and k == 'callback' \ 
    1074                             and not args: 
    1075                         args = ['context'] 
    1076                         attributes['with_context'] = True 
    1077                 else: 
    1078                     args = [] 
    1079                 v = lazy_attr_evaluator(v, args) 
    1080  
     1101            k = str(aliases.get(k, k)) 
     1102            v, options = cls.cast_attribute(ns, k, v) 
     1103            attributes.update(options) 
    10811104            attributes[k] = v 
    10821105        return attributes 
     
    11141137            data = {} 
    11151138            vars = {} 
     1139        def defined(vars, any=False): 
     1140            for var in vars.split(): 
     1141                defined = var in locals 
     1142                if any and defined: 
     1143                    return True 
     1144                elif not any and not defined: 
     1145                    return False 
     1146            return not any 
     1147 
    11161148        locals.update(vars) 
     1149        locals['defined'] = defined 
    11171150        locals['v'] = vars 
    11181151        locals['a'] = args 
     
    14381471 
    14391472    @classmethod 
    1440     def cast_attribute(cls, name, value): 
     1473    def cast_attribute(cls, namespace, name, value): 
    14411474        if name == 'parts': 
    14421475            return int(value) 
    1443         return super(Hostname, cls).cast_attribute(name, value) 
     1476        return super(Hostname, cls).cast_attribute(namespace, name, value) 
    14441477 
    14451478 
  • cly/trunk/cly/exceptions.py

    r575 r576  
    1515__all__ = ['Error', 'InvalidHelp', 'InvalidNodePath', 'InvalidAnonymousNode', 
    1616           'ParseError', 'UnexpectedEOL', 'InvalidToken', 'ValidationError', 
    17            'XMLParseError', 'EvaluationError'
     17           'XMLParseError'
    1818__docformat__ = 'restructuredtext en' 
    1919 
  • cly/trunk/cly/parser.py

    r572 r576  
    330330        context = self.context_factory(self, command, data) 
    331331 
    332         # Set context on all nodes in the grammar 
    333         for node in self.grammar.walk(): 
    334             node.context = self 
    335  
    336         try: 
    337             def parse(node, match): 
    338                 context.trail.append((node, match)) 
    339                 if match is not None: 
    340                     node.advance(context) 
    341                 node.selected(context, match) 
    342  
    343                 for subnode in node.next(context): 
    344                     if subnode.valid(context): 
    345                         submatch = subnode.match(context) 
    346                         if submatch is not None: 
    347                             return parse(subnode, submatch) 
    348                 else: 
    349                     return 
    350                 raise InvalidToken(context) 
    351  
    352             parse(self.grammar, None) 
    353             return context 
    354         finally: 
    355             # Unset context on grammar 
    356             for node in self.grammar.walk(): 
    357                 node.context = None 
     332        def parse(node, match): 
     333            context.trail.append((node, match)) 
     334            if match is not None: 
     335                node.advance(context) 
     336            node.selected(context, match) 
     337 
     338            for subnode in node.next(context): 
     339                if subnode.valid(context): 
     340                    submatch = subnode.match(context) 
     341                    if submatch is not None: 
     342                        return parse(subnode, submatch) 
     343            else: 
     344                return 
     345            raise InvalidToken(context) 
     346 
     347        parse(self.grammar, None) 
     348        return context 
    358349 
    359350    def merge(self, where, grammar): 
  • cly/trunk/cly/test.py

    r568 r576  
    163163        class Test(Node): 
    164164            @classmethod 
    165             def cast_attribute(cls, name, value): 
     165            def cast_attribute(cls, namespace, name, value): 
    166166                if name == 'test': 
    167                     return int(value) 
    168                 return value 
     167                    return int(value), {} 
     168                return value, {} 
    169169 
    170170        xml = StringIO("""<?xml version="1.0"?>