##// END OF EJS Templates
registrar: switch @command decorator to class...
Yuya Nishihara -
r32338:ec84db23 default
parent child Browse files
Show More
@@ -1,306 +1,303 b''
1 # registrar.py - utilities to register function for specific purpose
1 # registrar.py - utilities to register function for specific purpose
2 #
2 #
3 # Copyright FUJIWARA Katsunori <foozy@lares.dti.ne.jp> and others
3 # Copyright FUJIWARA Katsunori <foozy@lares.dti.ne.jp> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 from . import (
10 from . import (
11 error,
11 error,
12 pycompat,
12 pycompat,
13 util,
13 util,
14 )
14 )
15
15
16 class _funcregistrarbase(object):
16 class _funcregistrarbase(object):
17 """Base of decorator to register a function for specific purpose
17 """Base of decorator to register a function for specific purpose
18
18
19 This decorator stores decorated functions into own dict 'table'.
19 This decorator stores decorated functions into own dict 'table'.
20
20
21 The least derived class can be defined by overriding 'formatdoc',
21 The least derived class can be defined by overriding 'formatdoc',
22 for example::
22 for example::
23
23
24 class keyword(_funcregistrarbase):
24 class keyword(_funcregistrarbase):
25 _docformat = ":%s: %s"
25 _docformat = ":%s: %s"
26
26
27 This should be used as below:
27 This should be used as below:
28
28
29 keyword = registrar.keyword()
29 keyword = registrar.keyword()
30
30
31 @keyword('bar')
31 @keyword('bar')
32 def barfunc(*args, **kwargs):
32 def barfunc(*args, **kwargs):
33 '''Explanation of bar keyword ....
33 '''Explanation of bar keyword ....
34 '''
34 '''
35 pass
35 pass
36
36
37 In this case:
37 In this case:
38
38
39 - 'barfunc' is stored as 'bar' in '_table' of an instance 'keyword' above
39 - 'barfunc' is stored as 'bar' in '_table' of an instance 'keyword' above
40 - 'barfunc.__doc__' becomes ":bar: Explanation of bar keyword"
40 - 'barfunc.__doc__' becomes ":bar: Explanation of bar keyword"
41 """
41 """
42 def __init__(self, table=None):
42 def __init__(self, table=None):
43 if table is None:
43 if table is None:
44 self._table = {}
44 self._table = {}
45 else:
45 else:
46 self._table = table
46 self._table = table
47
47
48 def __call__(self, decl, *args, **kwargs):
48 def __call__(self, decl, *args, **kwargs):
49 return lambda func: self._doregister(func, decl, *args, **kwargs)
49 return lambda func: self._doregister(func, decl, *args, **kwargs)
50
50
51 def _doregister(self, func, decl, *args, **kwargs):
51 def _doregister(self, func, decl, *args, **kwargs):
52 name = self._getname(decl)
52 name = self._getname(decl)
53
53
54 if name in self._table:
54 if name in self._table:
55 msg = 'duplicate registration for name: "%s"' % name
55 msg = 'duplicate registration for name: "%s"' % name
56 raise error.ProgrammingError(msg)
56 raise error.ProgrammingError(msg)
57
57
58 if func.__doc__ and not util.safehasattr(func, '_origdoc'):
58 if func.__doc__ and not util.safehasattr(func, '_origdoc'):
59 doc = pycompat.sysbytes(func.__doc__).strip()
59 doc = pycompat.sysbytes(func.__doc__).strip()
60 func._origdoc = doc
60 func._origdoc = doc
61 func.__doc__ = pycompat.sysstr(self._formatdoc(decl, doc))
61 func.__doc__ = pycompat.sysstr(self._formatdoc(decl, doc))
62
62
63 self._table[name] = func
63 self._table[name] = func
64 self._extrasetup(name, func, *args, **kwargs)
64 self._extrasetup(name, func, *args, **kwargs)
65
65
66 return func
66 return func
67
67
68 def _parsefuncdecl(self, decl):
68 def _parsefuncdecl(self, decl):
69 """Parse function declaration and return the name of function in it
69 """Parse function declaration and return the name of function in it
70 """
70 """
71 i = decl.find('(')
71 i = decl.find('(')
72 if i >= 0:
72 if i >= 0:
73 return decl[:i]
73 return decl[:i]
74 else:
74 else:
75 return decl
75 return decl
76
76
77 def _getname(self, decl):
77 def _getname(self, decl):
78 """Return the name of the registered function from decl
78 """Return the name of the registered function from decl
79
79
80 Derived class should override this, if it allows more
80 Derived class should override this, if it allows more
81 descriptive 'decl' string than just a name.
81 descriptive 'decl' string than just a name.
82 """
82 """
83 return decl
83 return decl
84
84
85 _docformat = None
85 _docformat = None
86
86
87 def _formatdoc(self, decl, doc):
87 def _formatdoc(self, decl, doc):
88 """Return formatted document of the registered function for help
88 """Return formatted document of the registered function for help
89
89
90 'doc' is '__doc__.strip()' of the registered function.
90 'doc' is '__doc__.strip()' of the registered function.
91 """
91 """
92 return self._docformat % (decl, doc)
92 return self._docformat % (decl, doc)
93
93
94 def _extrasetup(self, name, func):
94 def _extrasetup(self, name, func):
95 """Execute exra setup for registered function, if needed
95 """Execute exra setup for registered function, if needed
96 """
96 """
97 pass
97 pass
98
98
99 def command(table):
99 class command(_funcregistrarbase):
100 """Returns a function object to be used as a decorator for making commands.
100 """Decorator to register a command function to table
101
101
102 This function receives a command table as its argument. The table should
102 This class receives a command table as its argument. The table should
103 be a dict.
103 be a dict.
104
104
105 The returned function can be used as a decorator for adding commands
105 The created object can be used as a decorator for adding commands to
106 to that command table. This function accepts multiple arguments to define
106 that command table. This accepts multiple arguments to define a command.
107 a command.
108
107
109 The first argument is the command name.
108 The first argument is the command name.
110
109
111 The options argument is an iterable of tuples defining command arguments.
110 The options argument is an iterable of tuples defining command arguments.
112 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
111 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
113
112
114 The synopsis argument defines a short, one line summary of how to use the
113 The synopsis argument defines a short, one line summary of how to use the
115 command. This shows up in the help output.
114 command. This shows up in the help output.
116
115
117 The norepo argument defines whether the command does not require a
116 The norepo argument defines whether the command does not require a
118 local repository. Most commands operate against a repository, thus the
117 local repository. Most commands operate against a repository, thus the
119 default is False.
118 default is False.
120
119
121 The optionalrepo argument defines whether the command optionally requires
120 The optionalrepo argument defines whether the command optionally requires
122 a local repository.
121 a local repository.
123
122
124 The inferrepo argument defines whether to try to find a repository from the
123 The inferrepo argument defines whether to try to find a repository from the
125 command line arguments. If True, arguments will be examined for potential
124 command line arguments. If True, arguments will be examined for potential
126 repository locations. See ``findrepo()``. If a repository is found, it
125 repository locations. See ``findrepo()``. If a repository is found, it
127 will be used.
126 will be used.
128 """
127 """
129 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
128
130 inferrepo=False):
129 def _doregister(self, func, name, options=(), synopsis=None,
131 def decorator(func):
130 norepo=False, optionalrepo=False, inferrepo=False):
131 if True:
132 func.norepo = norepo
132 func.norepo = norepo
133 func.optionalrepo = optionalrepo
133 func.optionalrepo = optionalrepo
134 func.inferrepo = inferrepo
134 func.inferrepo = inferrepo
135 if synopsis:
135 if synopsis:
136 table[name] = func, list(options), synopsis
136 self._table[name] = func, list(options), synopsis
137 else:
137 else:
138 table[name] = func, list(options)
138 self._table[name] = func, list(options)
139 return func
139 return func
140 return decorator
141
142 return cmd
143
140
144 class revsetpredicate(_funcregistrarbase):
141 class revsetpredicate(_funcregistrarbase):
145 """Decorator to register revset predicate
142 """Decorator to register revset predicate
146
143
147 Usage::
144 Usage::
148
145
149 revsetpredicate = registrar.revsetpredicate()
146 revsetpredicate = registrar.revsetpredicate()
150
147
151 @revsetpredicate('mypredicate(arg1, arg2[, arg3])')
148 @revsetpredicate('mypredicate(arg1, arg2[, arg3])')
152 def mypredicatefunc(repo, subset, x):
149 def mypredicatefunc(repo, subset, x):
153 '''Explanation of this revset predicate ....
150 '''Explanation of this revset predicate ....
154 '''
151 '''
155 pass
152 pass
156
153
157 The first string argument is used also in online help.
154 The first string argument is used also in online help.
158
155
159 Optional argument 'safe' indicates whether a predicate is safe for
156 Optional argument 'safe' indicates whether a predicate is safe for
160 DoS attack (False by default).
157 DoS attack (False by default).
161
158
162 Optional argument 'takeorder' indicates whether a predicate function
159 Optional argument 'takeorder' indicates whether a predicate function
163 takes ordering policy as the last argument.
160 takes ordering policy as the last argument.
164
161
165 'revsetpredicate' instance in example above can be used to
162 'revsetpredicate' instance in example above can be used to
166 decorate multiple functions.
163 decorate multiple functions.
167
164
168 Decorated functions are registered automatically at loading
165 Decorated functions are registered automatically at loading
169 extension, if an instance named as 'revsetpredicate' is used for
166 extension, if an instance named as 'revsetpredicate' is used for
170 decorating in extension.
167 decorating in extension.
171
168
172 Otherwise, explicit 'revset.loadpredicate()' is needed.
169 Otherwise, explicit 'revset.loadpredicate()' is needed.
173 """
170 """
174 _getname = _funcregistrarbase._parsefuncdecl
171 _getname = _funcregistrarbase._parsefuncdecl
175 _docformat = "``%s``\n %s"
172 _docformat = "``%s``\n %s"
176
173
177 def _extrasetup(self, name, func, safe=False, takeorder=False):
174 def _extrasetup(self, name, func, safe=False, takeorder=False):
178 func._safe = safe
175 func._safe = safe
179 func._takeorder = takeorder
176 func._takeorder = takeorder
180
177
181 class filesetpredicate(_funcregistrarbase):
178 class filesetpredicate(_funcregistrarbase):
182 """Decorator to register fileset predicate
179 """Decorator to register fileset predicate
183
180
184 Usage::
181 Usage::
185
182
186 filesetpredicate = registrar.filesetpredicate()
183 filesetpredicate = registrar.filesetpredicate()
187
184
188 @filesetpredicate('mypredicate()')
185 @filesetpredicate('mypredicate()')
189 def mypredicatefunc(mctx, x):
186 def mypredicatefunc(mctx, x):
190 '''Explanation of this fileset predicate ....
187 '''Explanation of this fileset predicate ....
191 '''
188 '''
192 pass
189 pass
193
190
194 The first string argument is used also in online help.
191 The first string argument is used also in online help.
195
192
196 Optional argument 'callstatus' indicates whether a predicate
193 Optional argument 'callstatus' indicates whether a predicate
197 implies 'matchctx.status()' at runtime or not (False, by
194 implies 'matchctx.status()' at runtime or not (False, by
198 default).
195 default).
199
196
200 Optional argument 'callexisting' indicates whether a predicate
197 Optional argument 'callexisting' indicates whether a predicate
201 implies 'matchctx.existing()' at runtime or not (False, by
198 implies 'matchctx.existing()' at runtime or not (False, by
202 default).
199 default).
203
200
204 'filesetpredicate' instance in example above can be used to
201 'filesetpredicate' instance in example above can be used to
205 decorate multiple functions.
202 decorate multiple functions.
206
203
207 Decorated functions are registered automatically at loading
204 Decorated functions are registered automatically at loading
208 extension, if an instance named as 'filesetpredicate' is used for
205 extension, if an instance named as 'filesetpredicate' is used for
209 decorating in extension.
206 decorating in extension.
210
207
211 Otherwise, explicit 'fileset.loadpredicate()' is needed.
208 Otherwise, explicit 'fileset.loadpredicate()' is needed.
212 """
209 """
213 _getname = _funcregistrarbase._parsefuncdecl
210 _getname = _funcregistrarbase._parsefuncdecl
214 _docformat = "``%s``\n %s"
211 _docformat = "``%s``\n %s"
215
212
216 def _extrasetup(self, name, func, callstatus=False, callexisting=False):
213 def _extrasetup(self, name, func, callstatus=False, callexisting=False):
217 func._callstatus = callstatus
214 func._callstatus = callstatus
218 func._callexisting = callexisting
215 func._callexisting = callexisting
219
216
220 class _templateregistrarbase(_funcregistrarbase):
217 class _templateregistrarbase(_funcregistrarbase):
221 """Base of decorator to register functions as template specific one
218 """Base of decorator to register functions as template specific one
222 """
219 """
223 _docformat = ":%s: %s"
220 _docformat = ":%s: %s"
224
221
225 class templatekeyword(_templateregistrarbase):
222 class templatekeyword(_templateregistrarbase):
226 """Decorator to register template keyword
223 """Decorator to register template keyword
227
224
228 Usage::
225 Usage::
229
226
230 templatekeyword = registrar.templatekeyword()
227 templatekeyword = registrar.templatekeyword()
231
228
232 @templatekeyword('mykeyword')
229 @templatekeyword('mykeyword')
233 def mykeywordfunc(repo, ctx, templ, cache, revcache, **args):
230 def mykeywordfunc(repo, ctx, templ, cache, revcache, **args):
234 '''Explanation of this template keyword ....
231 '''Explanation of this template keyword ....
235 '''
232 '''
236 pass
233 pass
237
234
238 The first string argument is used also in online help.
235 The first string argument is used also in online help.
239
236
240 'templatekeyword' instance in example above can be used to
237 'templatekeyword' instance in example above can be used to
241 decorate multiple functions.
238 decorate multiple functions.
242
239
243 Decorated functions are registered automatically at loading
240 Decorated functions are registered automatically at loading
244 extension, if an instance named as 'templatekeyword' is used for
241 extension, if an instance named as 'templatekeyword' is used for
245 decorating in extension.
242 decorating in extension.
246
243
247 Otherwise, explicit 'templatekw.loadkeyword()' is needed.
244 Otherwise, explicit 'templatekw.loadkeyword()' is needed.
248 """
245 """
249
246
250 class templatefilter(_templateregistrarbase):
247 class templatefilter(_templateregistrarbase):
251 """Decorator to register template filer
248 """Decorator to register template filer
252
249
253 Usage::
250 Usage::
254
251
255 templatefilter = registrar.templatefilter()
252 templatefilter = registrar.templatefilter()
256
253
257 @templatefilter('myfilter')
254 @templatefilter('myfilter')
258 def myfilterfunc(text):
255 def myfilterfunc(text):
259 '''Explanation of this template filter ....
256 '''Explanation of this template filter ....
260 '''
257 '''
261 pass
258 pass
262
259
263 The first string argument is used also in online help.
260 The first string argument is used also in online help.
264
261
265 'templatefilter' instance in example above can be used to
262 'templatefilter' instance in example above can be used to
266 decorate multiple functions.
263 decorate multiple functions.
267
264
268 Decorated functions are registered automatically at loading
265 Decorated functions are registered automatically at loading
269 extension, if an instance named as 'templatefilter' is used for
266 extension, if an instance named as 'templatefilter' is used for
270 decorating in extension.
267 decorating in extension.
271
268
272 Otherwise, explicit 'templatefilters.loadkeyword()' is needed.
269 Otherwise, explicit 'templatefilters.loadkeyword()' is needed.
273 """
270 """
274
271
275 class templatefunc(_templateregistrarbase):
272 class templatefunc(_templateregistrarbase):
276 """Decorator to register template function
273 """Decorator to register template function
277
274
278 Usage::
275 Usage::
279
276
280 templatefunc = registrar.templatefunc()
277 templatefunc = registrar.templatefunc()
281
278
282 @templatefunc('myfunc(arg1, arg2[, arg3])', argspec='arg1 arg2 arg3')
279 @templatefunc('myfunc(arg1, arg2[, arg3])', argspec='arg1 arg2 arg3')
283 def myfuncfunc(context, mapping, args):
280 def myfuncfunc(context, mapping, args):
284 '''Explanation of this template function ....
281 '''Explanation of this template function ....
285 '''
282 '''
286 pass
283 pass
287
284
288 The first string argument is used also in online help.
285 The first string argument is used also in online help.
289
286
290 If optional 'argspec' is defined, the function will receive 'args' as
287 If optional 'argspec' is defined, the function will receive 'args' as
291 a dict of named arguments. Otherwise 'args' is a list of positional
288 a dict of named arguments. Otherwise 'args' is a list of positional
292 arguments.
289 arguments.
293
290
294 'templatefunc' instance in example above can be used to
291 'templatefunc' instance in example above can be used to
295 decorate multiple functions.
292 decorate multiple functions.
296
293
297 Decorated functions are registered automatically at loading
294 Decorated functions are registered automatically at loading
298 extension, if an instance named as 'templatefunc' is used for
295 extension, if an instance named as 'templatefunc' is used for
299 decorating in extension.
296 decorating in extension.
300
297
301 Otherwise, explicit 'templater.loadfunction()' is needed.
298 Otherwise, explicit 'templater.loadfunction()' is needed.
302 """
299 """
303 _getname = _funcregistrarbase._parsefuncdecl
300 _getname = _funcregistrarbase._parsefuncdecl
304
301
305 def _extrasetup(self, name, func, argspec=None):
302 def _extrasetup(self, name, func, argspec=None):
306 func._argspec = argspec
303 func._argspec = argspec
General Comments 0
You need to be logged in to leave comments. Login now