Show More
@@ -1,128 +1,207 b'' | |||
|
1 | 1 | # registrar.py - utilities to register function for specific purpose |
|
2 | 2 | # |
|
3 | 3 | # Copyright FUJIWARA Katsunori <foozy@lares.dti.ne.jp> and others |
|
4 | 4 | # |
|
5 | 5 | # This software may be used and distributed according to the terms of the |
|
6 | 6 | # GNU General Public License version 2 or any later version. |
|
7 | 7 | |
|
8 | 8 | from __future__ import absolute_import |
|
9 | 9 | |
|
10 | 10 | from . import ( |
|
11 | 11 | util, |
|
12 | 12 | ) |
|
13 | 13 | |
|
14 | 14 | class funcregistrar(object): |
|
15 | 15 | """Base of decorator to register a fuction for specific purpose |
|
16 | 16 | |
|
17 | 17 | The least derived class can be defined by overriding 'table' and |
|
18 | 18 | 'formatdoc', for example:: |
|
19 | 19 | |
|
20 | 20 | symbols = {} |
|
21 | 21 | class keyword(funcregistrar): |
|
22 | 22 | table = symbols |
|
23 | 23 | formatdoc = ":%s: %s" |
|
24 | 24 | |
|
25 | 25 | @keyword('bar') |
|
26 | 26 | def barfunc(*args, **kwargs): |
|
27 | 27 | '''Explanation of bar keyword .... |
|
28 | 28 | ''' |
|
29 | 29 | pass |
|
30 | 30 | |
|
31 | 31 | In this case: |
|
32 | 32 | |
|
33 | 33 | - 'barfunc' is registered as 'bar' in 'symbols' |
|
34 | 34 | - online help uses ":bar: Explanation of bar keyword" |
|
35 | 35 | """ |
|
36 | 36 | |
|
37 | 37 | def __init__(self, decl): |
|
38 | 38 | """'decl' is a name or more descriptive string of a function |
|
39 | 39 | |
|
40 | 40 | Specification of 'decl' depends on registration purpose. |
|
41 | 41 | """ |
|
42 | 42 | self.decl = decl |
|
43 | 43 | |
|
44 | 44 | table = None |
|
45 | 45 | |
|
46 | 46 | def __call__(self, func): |
|
47 | 47 | """Execute actual registration for specified function |
|
48 | 48 | """ |
|
49 | 49 | name = self.getname() |
|
50 | 50 | |
|
51 | 51 | if func.__doc__ and not util.safehasattr(func, '_origdoc'): |
|
52 | 52 | doc = func.__doc__.strip() |
|
53 | 53 | func._origdoc = doc |
|
54 | 54 | if callable(self.formatdoc): |
|
55 | 55 | func.__doc__ = self.formatdoc(doc) |
|
56 | 56 | else: |
|
57 | 57 | # convenient shortcut for simple format |
|
58 | 58 | func.__doc__ = self.formatdoc % (self.decl, doc) |
|
59 | 59 | |
|
60 | 60 | self.table[name] = func |
|
61 | 61 | self.extraaction(name, func) |
|
62 | 62 | |
|
63 | 63 | return func |
|
64 | 64 | |
|
65 | 65 | def getname(self): |
|
66 | 66 | """Return the name of the registered function from self.decl |
|
67 | 67 | |
|
68 | 68 | Derived class should override this, if it allows more |
|
69 | 69 | descriptive 'decl' string than just a name. |
|
70 | 70 | """ |
|
71 | 71 | return self.decl |
|
72 | 72 | |
|
73 | 73 | def parsefuncdecl(self): |
|
74 | 74 | """Parse function declaration and return the name of function in it |
|
75 | 75 | """ |
|
76 | 76 | i = self.decl.find('(') |
|
77 | 77 | if i > 0: |
|
78 | 78 | return self.decl[:i] |
|
79 | 79 | else: |
|
80 | 80 | return self.decl |
|
81 | 81 | |
|
82 | 82 | def formatdoc(self, doc): |
|
83 | 83 | """Return formatted document of the registered function for help |
|
84 | 84 | |
|
85 | 85 | 'doc' is '__doc__.strip()' of the registered function. |
|
86 | 86 | |
|
87 | 87 | If this is overridden by non-callable object in derived class, |
|
88 | 88 | such value is treated as "format string" and used to format |
|
89 | 89 | document by 'self.formatdoc % (self.decl, doc)' for convenience. |
|
90 | 90 | """ |
|
91 | 91 | raise NotImplementedError() |
|
92 | 92 | |
|
93 | 93 | def extraaction(self, name, func): |
|
94 | 94 | """Execute exra action for registered function, if needed |
|
95 | 95 | """ |
|
96 | 96 | pass |
|
97 | 97 | |
|
98 | 98 | class delayregistrar(object): |
|
99 | 99 | """Decorator to delay actual registration until uisetup or so |
|
100 | 100 | |
|
101 | 101 | For example, the decorator class to delay registration by |
|
102 | 102 | 'keyword' funcregistrar can be defined as below:: |
|
103 | 103 | |
|
104 | 104 | class extkeyword(delayregistrar): |
|
105 | 105 | registrar = keyword |
|
106 | 106 | """ |
|
107 | 107 | def __init__(self): |
|
108 | 108 | self._list = [] |
|
109 | 109 | |
|
110 | 110 | registrar = None |
|
111 | 111 | |
|
112 | 112 | def __call__(self, *args, **kwargs): |
|
113 | 113 | """Return the decorator to delay actual registration until setup |
|
114 | 114 | """ |
|
115 | 115 | assert self.registrar is not None |
|
116 | 116 | def decorator(func): |
|
117 | 117 | # invocation of self.registrar() here can detect argument |
|
118 | 118 | # mismatching immediately |
|
119 | 119 | self._list.append((func, self.registrar(*args, **kwargs))) |
|
120 | 120 | return func |
|
121 | 121 | return decorator |
|
122 | 122 | |
|
123 | 123 | def setup(self): |
|
124 | 124 | """Execute actual registration |
|
125 | 125 | """ |
|
126 | 126 | while self._list: |
|
127 | 127 | func, decorator = self._list.pop(0) |
|
128 | 128 | decorator(func) |
|
129 | ||
|
130 | class _funcregistrarbase(object): | |
|
131 | """Base of decorator to register a fuction for specific purpose | |
|
132 | ||
|
133 | This decorator stores decorated functions into own dict 'table'. | |
|
134 | ||
|
135 | The least derived class can be defined by overriding 'formatdoc', | |
|
136 | for example:: | |
|
137 | ||
|
138 | class keyword(_funcregistrarbase): | |
|
139 | _docformat = ":%s: %s" | |
|
140 | ||
|
141 | This should be used as below: | |
|
142 | ||
|
143 | keyword = registrar.keyword() | |
|
144 | ||
|
145 | @keyword('bar') | |
|
146 | def barfunc(*args, **kwargs): | |
|
147 | '''Explanation of bar keyword .... | |
|
148 | ''' | |
|
149 | pass | |
|
150 | ||
|
151 | In this case: | |
|
152 | ||
|
153 | - 'barfunc' is stored as 'bar' in '_table' of an instance 'keyword' above | |
|
154 | - 'barfunc.__doc__' becomes ":bar: Explanation of bar keyword" | |
|
155 | """ | |
|
156 | def __init__(self, table=None): | |
|
157 | if table is None: | |
|
158 | self._table = {} | |
|
159 | else: | |
|
160 | self._table = table | |
|
161 | ||
|
162 | def __call__(self, decl, *args, **kwargs): | |
|
163 | return lambda func: self._doregister(func, decl, *args, **kwargs) | |
|
164 | ||
|
165 | def _doregister(self, func, decl, *args, **kwargs): | |
|
166 | name = self._getname(decl) | |
|
167 | ||
|
168 | if func.__doc__ and not util.safehasattr(func, '_origdoc'): | |
|
169 | doc = func.__doc__.strip() | |
|
170 | func._origdoc = doc | |
|
171 | func.__doc__ = self._formatdoc(decl, doc) | |
|
172 | ||
|
173 | self._table[name] = func | |
|
174 | self._extrasetup(name, func, *args, **kwargs) | |
|
175 | ||
|
176 | return func | |
|
177 | ||
|
178 | def _parsefuncdecl(self, decl): | |
|
179 | """Parse function declaration and return the name of function in it | |
|
180 | """ | |
|
181 | i = decl.find('(') | |
|
182 | if i >= 0: | |
|
183 | return decl[:i] | |
|
184 | else: | |
|
185 | return decl | |
|
186 | ||
|
187 | def _getname(self, decl): | |
|
188 | """Return the name of the registered function from decl | |
|
189 | ||
|
190 | Derived class should override this, if it allows more | |
|
191 | descriptive 'decl' string than just a name. | |
|
192 | """ | |
|
193 | return decl | |
|
194 | ||
|
195 | _docformat = None | |
|
196 | ||
|
197 | def _formatdoc(self, decl, doc): | |
|
198 | """Return formatted document of the registered function for help | |
|
199 | ||
|
200 | 'doc' is '__doc__.strip()' of the registered function. | |
|
201 | """ | |
|
202 | return self._docformat % (decl, doc) | |
|
203 | ||
|
204 | def _extrasetup(self, name, func): | |
|
205 | """Execute exra setup for registered function, if needed | |
|
206 | """ | |
|
207 | pass |
General Comments 0
You need to be logged in to leave comments.
Login now