##// 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 return t
94 return t
95
95
96 def splitargspec(spec):
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 >>> splitargspec('')
99 >>> splitargspec('')
100 ([], None, [])
100 ([], None, [], None)
101 >>> splitargspec('foo bar')
101 >>> splitargspec('foo bar')
102 ([], None, ['foo', 'bar'])
102 ([], None, ['foo', 'bar'], None)
103 >>> splitargspec('foo *bar baz')
103 >>> splitargspec('foo *bar baz **qux')
104 (['foo'], 'bar', ['baz'])
104 (['foo'], 'bar', ['baz'], 'qux')
105 >>> splitargspec('*foo')
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 pres = pre.split()
121 pres = pre.split()
110 posts = post.split()
122 posts = post.split()
111 if sep:
123 if sep:
112 if not posts:
124 if not posts:
113 raise error.ProgrammingError('no *varkey name provided')
125 raise error.ProgrammingError('no *varkey name provided')
114 return pres, posts[0], posts[1:]
126 return pres, posts[0], posts[1:], optkey
115 return [], None, pres
127 return [], None, pres, optkey
116
128
117 def buildargsdict(trees, funcname, argspec, keyvaluenode, keynode):
129 def buildargsdict(trees, funcname, argspec, keyvaluenode, keynode):
118 """Build dict from list containing positional and keyword arguments
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 - ``poskeys``: list of names of positional arguments
135 - ``poskeys``: list of names of positional arguments
123 - ``varkey``: optional argument name that takes up remainder
136 - ``varkey``: optional argument name that takes up remainder
124 - ``keys``: list of names that can be either positional or keyword arguments
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 If ``varkey`` specified, all ``keys`` must be given as keyword arguments.
140 If ``varkey`` specified, all ``keys`` must be given as keyword arguments.
127
141
128 Invalid keywords, too few positional arguments, or too many positional
142 Invalid keywords, too few positional arguments, or too many positional
129 arguments are rejected, but missing keyword arguments are just omitted.
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 kwstart = next((i for i, x in enumerate(trees) if x[0] == keyvaluenode),
146 kwstart = next((i for i, x in enumerate(trees) if x[0] == keyvaluenode),
133 len(trees))
147 len(trees))
134 if kwstart < len(poskeys):
148 if kwstart < len(poskeys):
@@ -150,20 +164,26 b' def buildargsdict(trees, funcname, argsp'
150 for k, x in zip(keys, trees[len(args):kwstart]):
164 for k, x in zip(keys, trees[len(args):kwstart]):
151 args[k] = x
165 args[k] = x
152 # remainder should be keyword arguments
166 # remainder should be keyword arguments
167 if optkey:
168 args[optkey] = {}
153 for x in trees[kwstart:]:
169 for x in trees[kwstart:]:
154 if x[0] != keyvaluenode or x[1][0] != keynode:
170 if x[0] != keyvaluenode or x[1][0] != keynode:
155 raise error.ParseError(_("%(func)s got an invalid argument")
171 raise error.ParseError(_("%(func)s got an invalid argument")
156 % {'func': funcname})
172 % {'func': funcname})
157 k = x[1][1]
173 k = x[1][1]
158 if k not in keys:
174 if k in keys:
175 d = args
176 elif not optkey:
159 raise error.ParseError(_("%(func)s got an unexpected keyword "
177 raise error.ParseError(_("%(func)s got an unexpected keyword "
160 "argument '%(key)s'")
178 "argument '%(key)s'")
161 % {'func': funcname, 'key': k})
179 % {'func': funcname, 'key': k})
162 if k in args:
180 else:
181 d = args[optkey]
182 if k in d:
163 raise error.ParseError(_("%(func)s got multiple values for keyword "
183 raise error.ParseError(_("%(func)s got multiple values for keyword "
164 "argument '%(key)s'")
184 "argument '%(key)s'")
165 % {'func': funcname, 'key': k})
185 % {'func': funcname, 'key': k})
166 args[k] = x[2]
186 d[k] = x[2]
167 return args
187 return args
168
188
169 def unescapestr(s):
189 def unescapestr(s):
@@ -467,7 +467,19 b' def buildfunc(exp, context):'
467
467
468 def _buildfuncargs(exp, context, curmethods, funcname, argspec):
468 def _buildfuncargs(exp, context, curmethods, funcname, argspec):
469 """Compile parsed tree of function arguments into list or dict of
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 def compiledict(xs):
483 def compiledict(xs):
472 return dict((k, compileexp(x, context, curmethods))
484 return dict((k, compileexp(x, context, curmethods))
473 for k, x in xs.iteritems())
485 for k, x in xs.iteritems())
@@ -479,12 +491,14 b' def _buildfuncargs(exp, context, curmeth'
479 return compilelist(getlist(exp))
491 return compilelist(getlist(exp))
480
492
481 # function with argspec: return dict of named args
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 treeargs = parser.buildargsdict(getlist(exp), funcname, argspec,
495 treeargs = parser.buildargsdict(getlist(exp), funcname, argspec,
484 keyvaluenode='keyvalue', keynode='symbol')
496 keyvaluenode='keyvalue', keynode='symbol')
485 compargs = {}
497 compargs = {}
486 if varkey:
498 if varkey:
487 compargs[varkey] = compilelist(treeargs.pop(varkey))
499 compargs[varkey] = compilelist(treeargs.pop(varkey))
500 if optkey:
501 compargs[optkey] = compiledict(treeargs.pop(optkey))
488 compargs.update(compiledict(treeargs))
502 compargs.update(compiledict(treeargs))
489 return compargs
503 return compargs
490
504
General Comments 0
You need to be logged in to leave comments. Login now