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)`` |
|
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 |
|
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 |
|
|
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