##// END OF EJS Templates
parser: extend buildargsdict() to support arbitrary number of **kwargs...
Yuya Nishihara -
r31921:2156934b default
parent child Browse files
Show More
@@ -94,41 +94,55 b' class parser(object):'
94 94 return t
95 95
96 96 def splitargspec(spec):
97 """Parse spec of function arguments into (poskeys, varkey, keys)
97 """Parse spec of function arguments into (poskeys, varkey, keys, optkey)
98 98
99 99 >>> splitargspec('')
100 ([], None, [])
100 ([], None, [], None)
101 101 >>> splitargspec('foo bar')
102 ([], None, ['foo', 'bar'])
103 >>> splitargspec('foo *bar baz')
104 (['foo'], 'bar', ['baz'])
102 ([], None, ['foo', 'bar'], None)
103 >>> splitargspec('foo *bar baz **qux')
104 (['foo'], 'bar', ['baz'], 'qux')
105 105 >>> splitargspec('*foo')
106 ([], 'foo', [])
106 ([], 'foo', [], None)
107 >>> splitargspec('**foo')
108 ([], None, [], 'foo')
107 109 """
108 pre, sep, post = spec.partition('*')
110 optkey = None
111 pre, sep, post = spec.partition('**')
112 if sep:
113 posts = post.split()
114 if not posts:
115 raise error.ProgrammingError('no **optkey name provided')
116 if len(posts) > 1:
117 raise error.ProgrammingError('excessive **optkey names provided')
118 optkey = posts[0]
119
120 pre, sep, post = pre.partition('*')
109 121 pres = pre.split()
110 122 posts = post.split()
111 123 if sep:
112 124 if not posts:
113 125 raise error.ProgrammingError('no *varkey name provided')
114 return pres, posts[0], posts[1:]
115 return [], None, pres
126 return pres, posts[0], posts[1:], optkey
127 return [], None, pres, optkey
116 128
117 129 def buildargsdict(trees, funcname, argspec, keyvaluenode, keynode):
118 130 """Build dict from list containing positional and keyword arguments
119 131
120 Arguments are specified by a tuple of ``(poskeys, varkey, keys)`` where
132 Arguments are specified by a tuple of ``(poskeys, varkey, keys, optkey)``
133 where
121 134
122 135 - ``poskeys``: list of names of positional arguments
123 136 - ``varkey``: optional argument name that takes up remainder
124 137 - ``keys``: list of names that can be either positional or keyword arguments
138 - ``optkey``: optional argument name that takes up excess keyword arguments
125 139
126 140 If ``varkey`` specified, all ``keys`` must be given as keyword arguments.
127 141
128 142 Invalid keywords, too few positional arguments, or too many positional
129 143 arguments are rejected, but missing keyword arguments are just omitted.
130 144 """
131 poskeys, varkey, keys = argspec
145 poskeys, varkey, keys, optkey = argspec
132 146 kwstart = next((i for i, x in enumerate(trees) if x[0] == keyvaluenode),
133 147 len(trees))
134 148 if kwstart < len(poskeys):
@@ -150,20 +164,26 b' def buildargsdict(trees, funcname, argsp'
150 164 for k, x in zip(keys, trees[len(args):kwstart]):
151 165 args[k] = x
152 166 # remainder should be keyword arguments
167 if optkey:
168 args[optkey] = {}
153 169 for x in trees[kwstart:]:
154 170 if x[0] != keyvaluenode or x[1][0] != keynode:
155 171 raise error.ParseError(_("%(func)s got an invalid argument")
156 172 % {'func': funcname})
157 173 k = x[1][1]
158 if k not in keys:
174 if k in keys:
175 d = args
176 elif not optkey:
159 177 raise error.ParseError(_("%(func)s got an unexpected keyword "
160 178 "argument '%(key)s'")
161 179 % {'func': funcname, 'key': k})
162 if k in args:
180 else:
181 d = args[optkey]
182 if k in d:
163 183 raise error.ParseError(_("%(func)s got multiple values for keyword "
164 184 "argument '%(key)s'")
165 185 % {'func': funcname, 'key': k})
166 args[k] = x[2]
186 d[k] = x[2]
167 187 return args
168 188
169 189 def unescapestr(s):
@@ -467,7 +467,19 b' def buildfunc(exp, context):'
467 467
468 468 def _buildfuncargs(exp, context, curmethods, funcname, argspec):
469 469 """Compile parsed tree of function arguments into list or dict of
470 (func, data) pairs"""
470 (func, data) pairs
471
472 >>> context = engine(lambda t: (runsymbol, t))
473 >>> def fargs(expr, argspec):
474 ... x = _parseexpr(expr)
475 ... n = getsymbol(x[1])
476 ... return _buildfuncargs(x[2], context, exprmethods, n, argspec)
477 >>> sorted(fargs('a(l=1, k=2)', 'k l m').keys())
478 ['k', 'l']
479 >>> args = fargs('a(opts=1, k=2)', '**opts')
480 >>> args.keys(), sorted(args['opts'].keys())
481 (['opts'], ['k', 'opts'])
482 """
471 483 def compiledict(xs):
472 484 return dict((k, compileexp(x, context, curmethods))
473 485 for k, x in xs.iteritems())
@@ -479,12 +491,14 b' def _buildfuncargs(exp, context, curmeth'
479 491 return compilelist(getlist(exp))
480 492
481 493 # function with argspec: return dict of named args
482 _poskeys, varkey, _keys = argspec = parser.splitargspec(argspec)
494 _poskeys, varkey, _keys, optkey = argspec = parser.splitargspec(argspec)
483 495 treeargs = parser.buildargsdict(getlist(exp), funcname, argspec,
484 496 keyvaluenode='keyvalue', keynode='symbol')
485 497 compargs = {}
486 498 if varkey:
487 499 compargs[varkey] = compilelist(treeargs.pop(varkey))
500 if optkey:
501 compargs[optkey] = compiledict(treeargs.pop(optkey))
488 502 compargs.update(compiledict(treeargs))
489 503 return compargs
490 504
General Comments 0
You need to be logged in to leave comments. Login now