Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/parser.py @ 31921:2156934b7917
parser: extend buildargsdict() to support arbitrary number of **kwargs
Prepares for adding dict(key1=value1, ...) template function. More tests
will be added later.
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Mon, 03 Apr 2017 22:07:09 +0900 |
parents | a98540ea1e42 |
children | 0f41f1e3c75c |
comparison
equal
deleted
inserted
replaced
31920:a98540ea1e42 | 31921:2156934b7917 |
---|---|
92 if self._methods: | 92 if self._methods: |
93 return self.eval(t) | 93 return self.eval(t) |
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): |
135 raise error.ParseError(_("%(func)s takes at least %(nargs)d positional " | 149 raise error.ParseError(_("%(func)s takes at least %(nargs)d positional " |
136 "arguments") | 150 "arguments") |
148 args[varkey] = trees[len(args):kwstart] | 162 args[varkey] = trees[len(args):kwstart] |
149 else: | 163 else: |
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): |
170 try: | 190 try: |
171 return util.unescapestr(s) | 191 return util.unescapestr(s) |