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)