registrar.py
240 lines
| 6.9 KiB
| text/x-python
|
PythonLexer
/ mercurial / registrar.py
FUJIWARA Katsunori
|
r27583 | # registrar.py - utilities to register function for specific purpose | ||
# | ||||
# Copyright FUJIWARA Katsunori <foozy@lares.dti.ne.jp> and others | ||||
# | ||||
# This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | ||||
from __future__ import absolute_import | ||||
from . import ( | ||||
util, | ||||
) | ||||
class funcregistrar(object): | ||||
"""Base of decorator to register a fuction for specific purpose | ||||
The least derived class can be defined by overriding 'table' and | ||||
'formatdoc', for example:: | ||||
symbols = {} | ||||
class keyword(funcregistrar): | ||||
table = symbols | ||||
formatdoc = ":%s: %s" | ||||
@keyword('bar') | ||||
def barfunc(*args, **kwargs): | ||||
'''Explanation of bar keyword .... | ||||
''' | ||||
pass | ||||
In this case: | ||||
- 'barfunc' is registered as 'bar' in 'symbols' | ||||
- online help uses ":bar: Explanation of bar keyword" | ||||
""" | ||||
def __init__(self, decl): | ||||
"""'decl' is a name or more descriptive string of a function | ||||
Specification of 'decl' depends on registration purpose. | ||||
""" | ||||
self.decl = decl | ||||
table = None | ||||
def __call__(self, func): | ||||
"""Execute actual registration for specified function | ||||
""" | ||||
name = self.getname() | ||||
if func.__doc__ and not util.safehasattr(func, '_origdoc'): | ||||
doc = func.__doc__.strip() | ||||
func._origdoc = doc | ||||
if callable(self.formatdoc): | ||||
func.__doc__ = self.formatdoc(doc) | ||||
else: | ||||
# convenient shortcut for simple format | ||||
func.__doc__ = self.formatdoc % (self.decl, doc) | ||||
self.table[name] = func | ||||
self.extraaction(name, func) | ||||
return func | ||||
def getname(self): | ||||
"""Return the name of the registered function from self.decl | ||||
Derived class should override this, if it allows more | ||||
descriptive 'decl' string than just a name. | ||||
""" | ||||
return self.decl | ||||
FUJIWARA Katsunori
|
r27584 | def parsefuncdecl(self): | ||
"""Parse function declaration and return the name of function in it | ||||
""" | ||||
i = self.decl.find('(') | ||||
if i > 0: | ||||
return self.decl[:i] | ||||
else: | ||||
return self.decl | ||||
FUJIWARA Katsunori
|
r27583 | def formatdoc(self, doc): | ||
"""Return formatted document of the registered function for help | ||||
'doc' is '__doc__.strip()' of the registered function. | ||||
If this is overridden by non-callable object in derived class, | ||||
such value is treated as "format string" and used to format | ||||
document by 'self.formatdoc % (self.decl, doc)' for convenience. | ||||
""" | ||||
raise NotImplementedError() | ||||
def extraaction(self, name, func): | ||||
"""Execute exra action for registered function, if needed | ||||
""" | ||||
pass | ||||
FUJIWARA Katsunori
|
r27585 | |||
class delayregistrar(object): | ||||
"""Decorator to delay actual registration until uisetup or so | ||||
For example, the decorator class to delay registration by | ||||
'keyword' funcregistrar can be defined as below:: | ||||
class extkeyword(delayregistrar): | ||||
registrar = keyword | ||||
""" | ||||
def __init__(self): | ||||
self._list = [] | ||||
registrar = None | ||||
def __call__(self, *args, **kwargs): | ||||
"""Return the decorator to delay actual registration until setup | ||||
""" | ||||
assert self.registrar is not None | ||||
def decorator(func): | ||||
# invocation of self.registrar() here can detect argument | ||||
# mismatching immediately | ||||
self._list.append((func, self.registrar(*args, **kwargs))) | ||||
return func | ||||
return decorator | ||||
def setup(self): | ||||
"""Execute actual registration | ||||
""" | ||||
while self._list: | ||||
func, decorator = self._list.pop(0) | ||||
decorator(func) | ||||
FUJIWARA Katsunori
|
r28392 | |||
class _funcregistrarbase(object): | ||||
"""Base of decorator to register a fuction for specific purpose | ||||
This decorator stores decorated functions into own dict 'table'. | ||||
The least derived class can be defined by overriding 'formatdoc', | ||||
for example:: | ||||
class keyword(_funcregistrarbase): | ||||
_docformat = ":%s: %s" | ||||
This should be used as below: | ||||
keyword = registrar.keyword() | ||||
@keyword('bar') | ||||
def barfunc(*args, **kwargs): | ||||
'''Explanation of bar keyword .... | ||||
''' | ||||
pass | ||||
In this case: | ||||
- 'barfunc' is stored as 'bar' in '_table' of an instance 'keyword' above | ||||
- 'barfunc.__doc__' becomes ":bar: Explanation of bar keyword" | ||||
""" | ||||
def __init__(self, table=None): | ||||
if table is None: | ||||
self._table = {} | ||||
else: | ||||
self._table = table | ||||
def __call__(self, decl, *args, **kwargs): | ||||
return lambda func: self._doregister(func, decl, *args, **kwargs) | ||||
def _doregister(self, func, decl, *args, **kwargs): | ||||
name = self._getname(decl) | ||||
if func.__doc__ and not util.safehasattr(func, '_origdoc'): | ||||
doc = func.__doc__.strip() | ||||
func._origdoc = doc | ||||
func.__doc__ = self._formatdoc(decl, doc) | ||||
self._table[name] = func | ||||
self._extrasetup(name, func, *args, **kwargs) | ||||
return func | ||||
def _parsefuncdecl(self, decl): | ||||
"""Parse function declaration and return the name of function in it | ||||
""" | ||||
i = decl.find('(') | ||||
if i >= 0: | ||||
return decl[:i] | ||||
else: | ||||
return decl | ||||
def _getname(self, decl): | ||||
"""Return the name of the registered function from decl | ||||
Derived class should override this, if it allows more | ||||
descriptive 'decl' string than just a name. | ||||
""" | ||||
return decl | ||||
_docformat = None | ||||
def _formatdoc(self, decl, doc): | ||||
"""Return formatted document of the registered function for help | ||||
'doc' is '__doc__.strip()' of the registered function. | ||||
""" | ||||
return self._docformat % (decl, doc) | ||||
def _extrasetup(self, name, func): | ||||
"""Execute exra setup for registered function, if needed | ||||
""" | ||||
pass | ||||
FUJIWARA Katsunori
|
r28393 | |||
class revsetpredicate(_funcregistrarbase): | ||||
"""Decorator to register revset predicate | ||||
Usage:: | ||||
revsetpredicate = registrar.revsetpredicate() | ||||
@revsetpredicate('mypredicate(arg1, arg2[, arg3])') | ||||
def mypredicatefunc(repo, subset, x): | ||||
'''Explanation of this revset predicate .... | ||||
''' | ||||
pass | ||||
The first string argument is used also in online help. | ||||
Optional argument 'safe' indicates whether a predicate is safe for | ||||
DoS attack (False by default). | ||||
'revsetpredicate' instance in example above can be used to | ||||
decorate multiple functions. | ||||
Decorated functions are registered automatically at loading | ||||
extension, if an instance named as 'revsetpredicate' is used for | ||||
decorating in extension. | ||||
Otherwise, explicit 'revset.loadpredicate()' is needed. | ||||
""" | ||||
_getname = _funcregistrarbase._parsefuncdecl | ||||
_docformat = "``%s``\n %s" | ||||
def _extrasetup(self, name, func, safe=False): | ||||
func._safe = safe | ||||