|
24.1.1
by Gary Poster
initial checkin of abstracted version |
1 |
# Copyright 2004-2009 Canonical Ltd. All rights reserved.
|
|
1
by Barry Warsaw
Initial package template for lazr packages. |
2 |
#
|
|
24
by Leonard Richardson
Initial preparation. |
3 |
# This file is part of lazr.enum
|
|
1
by Barry Warsaw
Initial package template for lazr packages. |
4 |
#
|
|
24
by Leonard Richardson
Initial preparation. |
5 |
# lazr.enum is free software: you can redistribute it and/or modify it
|
|
1
by Barry Warsaw
Initial package template for lazr packages. |
6 |
# under the terms of the GNU Lesser General Public License as published by
|
7 |
# the Free Software Foundation, either version 3 of the License, or (at your
|
|
8 |
# option) any later version.
|
|
9 |
#
|
|
|
24
by Leonard Richardson
Initial preparation. |
10 |
# lazr.enum is distributed in the hope that it will be useful, but WITHOUT
|
|
1
by Barry Warsaw
Initial package template for lazr packages. |
11 |
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
12 |
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
13 |
# License for more details.
|
|
14 |
#
|
|
15 |
# You should have received a copy of the GNU Lesser General Public License
|
|
|
24
by Leonard Richardson
Initial preparation. |
16 |
# along with lazr.enum. If not, see <http://www.gnu.org/licenses/>.
|
|
1
by Barry Warsaw
Initial package template for lazr packages. |
17 |
|
|
24.1.1
by Gary Poster
initial checkin of abstracted version |
18 |
__metaclass__ = type |
19 |
||
20 |
import itertools |
|
21 |
import operator |
|
22 |
import sys |
|
23 |
import warnings |
|
24 |
||
25 |
from zope.interface import implements |
|
26 |
from zope.schema.interfaces import ITitledTokenizedTerm, IVocabularyTokenized |
|
27 |
try: |
|
28 |
from zope.proxy import removeAllProxies |
|
29 |
except ImportError: |
|
30 |
removeAllProxies = lambda obj: obj # no-op |
|
31 |
||
32 |
from lazr.enum.interfaces import IEnumeratedType |
|
33 |
||
34 |
__all__ = [ |
|
35 |
'BaseItem', |
|
36 |
'DBEnumeratedType', |
|
37 |
'DBItem', |
|
38 |
'EnumeratedType', |
|
39 |
'IEnumeratedType', |
|
40 |
'Item', |
|
41 |
'TokenizedItem', |
|
42 |
'enumerated_type_registry', |
|
43 |
'use_template', |
|
|
25.1.1
by Gary Poster
make license only v3 of LGPL. update to lazr.yourpkg current patterns. |
44 |
'proxy_isinstance', |
|
25.1.2
by Gary Poster
reexport more items |
45 |
'MetaEnum', # needed for configure.zcml |
46 |
'MetaDBEnum', # needed for configure.zcml |
|
|
24.1.1
by Gary Poster
initial checkin of abstracted version |
47 |
]
|
48 |
||
49 |
def proxy_isinstance(obj, cls): |
|
50 |
"""Test whether an object is an instance of a type.
|
|
51 |
||
52 |
This works even if the object is proxied by zope.proxy, if that package
|
|
53 |
is available.
|
|
54 |
"""
|
|
55 |
return isinstance(removeAllProxies(obj), cls) |
|
56 |
||
57 |
def docstring_to_title_descr(string): |
|
58 |
"""When given a classically formatted docstring, returns a tuple
|
|
59 |
(title, description).
|
|
60 |
||
61 |
>>> class Foo:
|
|
62 |
... '''
|
|
63 |
... Title of foo
|
|
64 |
...
|
|
65 |
... Description of foo starts here. It may
|
|
66 |
... spill onto multiple lines. It may also have
|
|
67 |
... indented examples:
|
|
68 |
...
|
|
69 |
... Foo
|
|
70 |
... Bar
|
|
71 |
...
|
|
72 |
... like the above.
|
|
73 |
... '''
|
|
74 |
...
|
|
75 |
>>> title, descr = docstring_to_title_descr(Foo.__doc__)
|
|
76 |
>>> print title
|
|
77 |
Title of foo
|
|
78 |
>>> for num, line in enumerate(descr.splitlines()):
|
|
79 |
... print "%d.%s" % (num, line)
|
|
80 |
...
|
|
81 |
0.Description of foo starts here. It may
|
|
82 |
1.spill onto multiple lines. It may also have
|
|
83 |
2.indented examples:
|
|
84 |
3.
|
|
85 |
4. Foo
|
|
86 |
5. Bar
|
|
87 |
6.
|
|
88 |
7.like the above.
|
|
89 |
||
90 |
"""
|
|
91 |
lines = string.splitlines() |
|
92 |
# title is the first non-blank line
|
|
93 |
for num, line in enumerate(lines): |
|
94 |
line = line.strip() |
|
95 |
if line: |
|
96 |
title = line |
|
97 |
break
|
|
98 |
else: |
|
99 |
raise ValueError |
|
100 |
assert not lines[num+1].strip() |
|
101 |
descrlines = lines[num+2:] |
|
102 |
descr1 = descrlines[0] |
|
103 |
indent = len(descr1) - len(descr1.lstrip()) |
|
104 |
descr = '\n'.join([line[indent:] for line in descrlines]) |
|
105 |
return title, descr |
|
106 |
||
107 |
||
108 |
class BaseItem: |
|
109 |
"""Items are the primary elements of the enumerated types.
|
|
110 |
||
111 |
`BaseItem` is the base class for both `Item` and `DBItem`.
|
|
112 |
||
113 |
The enum attribute is a reference to the enumerated type that the
|
|
114 |
Item is a member of.
|
|
115 |
||
116 |
The token attribute is the name assigned to the item.
|
|
117 |
||
118 |
The value is the short text string used to identify the item.
|
|
119 |
"""
|
|
120 |
||
121 |
sortkey = 0 |
|
122 |
name = None |
|
123 |
description = None |
|
124 |
title = None |
|
|
27.1.1
by Barry Warsaw
Support for Item urls. |
125 |
url = None |
|
24.1.1
by Gary Poster
initial checkin of abstracted version |
126 |
|
|
27.1.1
by Barry Warsaw
Support for Item urls. |
127 |
def __init__(self, title, description=None, url=None): |
|
24.1.1
by Gary Poster
initial checkin of abstracted version |
128 |
"""Items are the main elements of the EnumeratedType.
|
129 |
||
130 |
Where the title is passed in without a description,
|
|
131 |
and the title looks like a docstring (has embedded carriage returns),
|
|
132 |
the title is the first line, and the description is the rest.
|
|
133 |
"""
|
|
134 |
||
135 |
self.sortkey = BaseItem.sortkey |
|
136 |
BaseItem.sortkey += 1 |
|
137 |
self.title = title |
|
138 |
# The enum attribute is set duing the class constructor of the
|
|
139 |
# containing enumerated type.
|
|
140 |
||
141 |
self.description = description |
|
|
27.1.1
by Barry Warsaw
Support for Item urls. |
142 |
self.url = url |
|
24.1.1
by Gary Poster
initial checkin of abstracted version |
143 |
|
144 |
if self.description is None: |
|
145 |
# check value
|
|
146 |
if self.title.find('\n') != -1: |
|
147 |
self.title, self.description = docstring_to_title_descr( |
|
148 |
self.title) |
|
149 |
||
150 |
def __int__(self): |
|
151 |
raise TypeError("Cannot cast Item to int.") |
|
152 |
||
153 |
def __cmp__(self, other): |
|
154 |
if proxy_isinstance(other, BaseItem): |
|
155 |
return cmp(self.sortkey, other.sortkey) |
|
156 |
else: |
|
157 |
raise TypeError( |
|
158 |
'Comparisons of Items are only valid with other Items') |
|
159 |
||
160 |
def __eq__(self, other, stacklevel=2): |
|
161 |
if isinstance(other, int): |
|
162 |
warnings.warn('comparison of Item to an int: %r' % self, |
|
163 |
stacklevel=stacklevel) |
|
164 |
return False |
|
165 |
elif proxy_isinstance(other, BaseItem): |
|
166 |
return (self.name == other.name and |
|
167 |
self.enum == other.enum) |
|
168 |
else: |
|
169 |
return False |
|
170 |
||
171 |
def __ne__(self, other): |
|
172 |
return not self.__eq__(other, stacklevel=3) |
|
173 |
||
174 |
def __hash__(self): |
|
175 |
return hash(self.title) |
|
176 |
||
177 |
def __str__(self): |
|
178 |
return str(self.title) |
|
179 |
||
180 |
||
181 |
class Item(BaseItem): |
|
182 |
"""The `Item` is an element of an `EnumeratedType`."""
|
|
183 |
@staticmethod
|
|
184 |
def construct(other_item): |
|
185 |
"""Create an Item based on the other_item."""
|
|
|
27.1.3
by Barry Warsaw
DBItem override the base class constructor. Make sure DBItem also supports |
186 |
item = Item(other_item.title, other_item.description, other_item.url) |
|
24.1.1
by Gary Poster
initial checkin of abstracted version |
187 |
item.sortkey = other_item.sortkey |
188 |
return item |
|
189 |
||
190 |
def __repr__(self): |
|
191 |
return "<Item %s.%s, %s>" % ( |
|
192 |
self.enum.name, self.name, self.title) |
|
193 |
||
194 |
||
195 |
class DBItem(BaseItem): |
|
196 |
"""The `DBItem` refers to an enumerated item that is used in the database.
|
|
197 |
||
198 |
Database enumerations are stored in the database using integer columns.
|
|
199 |
"""
|
|
200 |
||
201 |
@staticmethod
|
|
202 |
def construct(other_item): |
|
203 |
"""Create an Item based on the other_item."""
|
|
|
27.1.3
by Barry Warsaw
DBItem override the base class constructor. Make sure DBItem also supports |
204 |
item = DBItem(other_item.value, other_item.title, |
205 |
other_item.description, other_item.url) |
|
|
24.1.1
by Gary Poster
initial checkin of abstracted version |
206 |
item.sortkey = other_item.sortkey |
207 |
return item |
|
208 |
||
|
27.1.3
by Barry Warsaw
DBItem override the base class constructor. Make sure DBItem also supports |
209 |
def __init__(self, value, title, description=None, url=None): |
210 |
BaseItem.__init__(self, title, description, url) |
|
|
24.1.1
by Gary Poster
initial checkin of abstracted version |
211 |
self.value = value |
212 |
||
213 |
def __hash__(self): |
|
214 |
return self.value |
|
215 |
||
216 |
def __repr__(self): |
|
217 |
return "<DBItem %s.%s, (%d) %s>" % ( |
|
218 |
self.enum.name, self.name, self.value, self.title) |
|
219 |
||
|
24.1.2
by Gary Poster
add configure.zcml; hook up the rest of the tests; comment that __sqlrepr__ may be going away. |
220 |
# XXX this is probably going away.
|
|
24.1.1
by Gary Poster
initial checkin of abstracted version |
221 |
def __sqlrepr__(self, dbname): |
222 |
return repr(self.value) |
|
223 |
||
224 |
||
225 |
class TokenizedItem: |
|
226 |
"""Wraps an `Item` or `DBItem` to provide `ITitledTokenizedTerm`."""
|
|
227 |
||
228 |
implements(ITitledTokenizedTerm) |
|
229 |
||
230 |
def __init__(self, item): |
|
231 |
self.value = item |
|
232 |
self.token = item.name |
|
233 |
self.title = item.title |
|
234 |
||
235 |
||
236 |
# The enumerated_type_registry is a mapping of all enumerated types to the
|
|
237 |
# actual class. There should only ever be one EnumeratedType or
|
|
238 |
# DBEnumerateType with a particular name. This serves two purposes:
|
|
|
24.1.3
by Gary Poster
address review: new lines, comment clean-up |
239 |
# * a way to get any enumerated type by its name
|
|
24.1.1
by Gary Poster
initial checkin of abstracted version |
240 |
# * a way to iterate over the DBEnumeratedTypes in order to confirm the
|
241 |
# values actually stored in the database.
|
|
242 |
enumerated_type_registry = {} |
|
243 |
||
244 |
||
245 |
class EnumItems: |
|
246 |
"""Allow access to Items of an enumerated type using names or db values.
|
|
247 |
||
248 |
Access can be made to the items using the name of the Item.
|
|
249 |
||
250 |
If the enumerated type has DBItems then the mapping includes a mapping of
|
|
251 |
the database integer values to the DBItems.
|
|
252 |
"""
|
|
253 |
def __init__(self, items, mapping): |
|
254 |
self.items = items |
|
255 |
self.mapping = mapping |
|
256 |
def __getitem__(self, key): |
|
257 |
if key in self.mapping: |
|
258 |
return self.mapping[key] |
|
259 |
else: |
|
260 |
raise KeyError(key) |
|
261 |
def __iter__(self): |
|
262 |
return self.items.__iter__() |
|
263 |
def __len__(self): |
|
264 |
return len(self.items) |
|
265 |
||
266 |
||
267 |
class BaseMetaEnum(type): |
|
268 |
"""The metaclass functionality for `EnumeratedType` and `DBEnumeratedType`.
|
|
269 |
||
270 |
This metaclass defines methods that allow the enumerated types to implement
|
|
271 |
the IVocabularyTokenized interface.
|
|
272 |
||
273 |
The metaclass also enforces "correct" definitions of enumerated types by
|
|
274 |
enforcing capitalisation of Item variable names and defining an appropriate
|
|
275 |
ordering.
|
|
276 |
"""
|
|
277 |
implements(IEnumeratedType, IVocabularyTokenized) |
|
278 |
||
279 |
@classmethod
|
|
280 |
def _enforceSingleInheritance(cls, classname, bases, classdict): |
|
281 |
"""Only one base class is allowed for enumerated types."""
|
|
282 |
if len(bases) > 1: |
|
283 |
raise TypeError( |
|
284 |
'Multiple inheritance is not allowed with '
|
|
285 |
'%s, %s.%s' % ( |
|
286 |
cls.enum_name, classdict['__module__'], classname)) |
|
287 |
||
288 |
@classmethod
|
|
289 |
def _updateClassDictWithBaseItems(cls, bases, classdict): |
|
290 |
"""Copy each of the items from the base class that hasn't been
|
|
291 |
explicitly defined in the new class."""
|
|
292 |
if bases: |
|
293 |
base_class = bases[0] |
|
294 |
if hasattr(base_class, 'items'): |
|
295 |
for item in base_class.items: |
|
296 |
if item.name not in classdict: |
|
297 |
new_item = cls.item_type.construct(item) |
|
298 |
classdict[item.name] = new_item |
|
299 |
||
300 |
@classmethod
|
|
301 |
def _updateClassDictWithTemplateItems(cls, classdict): |
|
302 |
"""If constructed through use_template, we need to construct
|
|
303 |
the appropriate type of items based on our item_type of our class."""
|
|
304 |
if 'template_items' in classdict: |
|
305 |
for item in classdict['template_items']: |
|
306 |
classdict[item.name] = cls.item_type.construct(item) |
|
307 |
# The template_items key is not wanted or needed in the new type.
|
|
308 |
del classdict['template_items'] |
|
309 |
||
310 |
@classmethod
|
|
311 |
def _enforceItemClassAndName(cls, items, classname, module_name): |
|
312 |
"""All items must be of the appropriate type for the enumeration type.
|
|
313 |
||
314 |
All item variable names must be capitalised.
|
|
315 |
"""
|
|
316 |
for item_name, item in items: |
|
317 |
if not isinstance(item, cls.item_type): |
|
318 |
raise TypeError( |
|
319 |
'Items must be of the appropriate type for the '
|
|
320 |
'%s, %s.%s.%s' % ( |
|
321 |
cls.enum_name, module_name, classname, item_name)) |
|
322 |
||
323 |
if item_name.upper() != item_name: |
|
324 |
raise TypeError( |
|
325 |
'Item instance variable names must be capitalised.'
|
|
326 |
' %s.%s.%s' % (module_name, classname, item_name)) |
|
327 |
||
328 |
item.name = item_name |
|
329 |
||
330 |
@classmethod
|
|
331 |
def _generateItemMapping(cls, items): |
|
332 |
"""Each enumerated type has a mapping of the item names to the item
|
|
333 |
instances."""
|
|
334 |
return dict(items) |
|
335 |
||
336 |
@classmethod
|
|
337 |
def _enforceSortOrder(cls, classname, classdict, items): |
|
338 |
""" Override item's default sort order if sort_order is defined.
|
|
339 |
||
340 |
:return: A list of items ordered appropriately.
|
|
341 |
"""
|
|
342 |
items = dict(items) |
|
343 |
if 'sort_order' in classdict: |
|
344 |
sort_order = classdict['sort_order'] |
|
345 |
item_names = sorted(items.keys()) |
|
346 |
if item_names != sorted(sort_order): |
|
347 |
raise TypeError( |
|
348 |
'sort_order for %s must contain all and ' |
|
349 |
'only Item instances %s.%s' % ( |
|
350 |
cls.enum_name, classdict['__module__'], classname)) |
|
351 |
else: |
|
352 |
# Sort the items by the automatically generated
|
|
353 |
# sortkey.
|
|
354 |
sort_order = [ |
|
355 |
item.name for item in sorted( |
|
356 |
items.values(), key=operator.attrgetter('sortkey'))] |
|
357 |
classdict['sort_order'] = tuple(sort_order) |
|
358 |
# Assign new sortkey values from zero.
|
|
359 |
sorted_items = [] |
|
360 |
for sort_id, item_name in enumerate(sort_order): |
|
361 |
item = classdict[item_name] |
|
362 |
item.sortkey = sort_id |
|
363 |
sorted_items.append(item) |
|
364 |
return sorted_items |
|
365 |
||
366 |
def __new__(cls, classname, bases, classdict): |
|
367 |
"""Called when defining a new class."""
|
|
368 |
||
369 |
cls._enforceSingleInheritance(classname, bases, classdict) |
|
370 |
cls._updateClassDictWithBaseItems(bases, classdict) |
|
371 |
cls._updateClassDictWithTemplateItems(classdict) |
|
372 |
||
373 |
items = [(key, value) for key, value in classdict.iteritems() |
|
374 |
if isinstance(value, BaseItem)] |
|
375 |
||
376 |
cls._enforceItemClassAndName(items, classname, classdict['__module__']) |
|
377 |
||
378 |
mapping = cls._generateItemMapping(items) |
|
379 |
sorted_items = cls._enforceSortOrder(classname, classdict, items) |
|
380 |
||
381 |
classdict['items'] = EnumItems(sorted_items, mapping) |
|
382 |
classdict['name'] = classname |
|
383 |
classdict['description'] = classdict.get('__doc__', None) |
|
384 |
||
385 |
global enumerated_type_registry |
|
386 |
if classname in enumerated_type_registry: |
|
387 |
other = enumerated_type_registry[classname] |
|
388 |
raise TypeError( |
|
389 |
'An enumerated type already exists with the name %s (%s.%s).' |
|
390 |
% (classname, other.__module__, other.name)) |
|
391 |
||
392 |
instance = type.__new__(cls, classname, bases, classdict) |
|
393 |
||
394 |
# Add a reference to the enumerated type to each item.
|
|
395 |
for item in instance.items: |
|
396 |
item.enum = instance |
|
397 |
||
398 |
# Add the enumerated type to the registry.
|
|
399 |
enumerated_type_registry[classname] = instance |
|
400 |
||
401 |
return instance |
|
402 |
||
403 |
def __contains__(self, value): |
|
404 |
"""See `ISource`."""
|
|
405 |
return value in self.items |
|
406 |
||
407 |
def __iter__(self): |
|
408 |
"""See `IIterableVocabulary`."""
|
|
409 |
return itertools.imap(TokenizedItem, self.items) |
|
410 |
||
411 |
def __len__(self): |
|
412 |
"""See `IIterableVocabulary`."""
|
|
413 |
return len(self.items) |
|
414 |
||
415 |
def getTerm(self, value): |
|
416 |
"""See `IBaseVocabulary`."""
|
|
417 |
if value in self.items: |
|
418 |
return TokenizedItem(value) |
|
419 |
raise LookupError(value) |
|
420 |
||
421 |
def getTermByToken(self, token): |
|
422 |
"""See `IVocabularyTokenized`."""
|
|
423 |
# The sort_order of the enumerated type lists all the items.
|
|
|
30.1.1
by Tim Penhey
Make the token checking case insensitive. |
424 |
upper_token = token.upper() |
425 |
if upper_token in self.sort_order: |
|
426 |
return TokenizedItem(getattr(self, upper_token)) |
|
|
24.1.1
by Gary Poster
initial checkin of abstracted version |
427 |
else: |
428 |
# If the token is not specified in the sort order then check
|
|
429 |
# the titles of the items. This is to support the transition
|
|
430 |
# of accessing items by their titles. To continue support
|
|
431 |
# of old URLs et al, this will probably stay for some time.
|
|
432 |
for item in self.items: |
|
433 |
if item.title == token: |
|
434 |
return TokenizedItem(item) |
|
435 |
# The token was not in the sort_order (and hence the name of a
|
|
436 |
# variable), nor was the token the title of one of the items.
|
|
437 |
raise LookupError(token) |
|
438 |
||
439 |
||
440 |
class MetaEnum(BaseMetaEnum): |
|
441 |
"""The metaclass for `EnumeratedType`."""
|
|
442 |
item_type = Item |
|
443 |
enum_name = 'EnumeratedType' |
|
444 |
||
445 |
def __repr__(self): |
|
446 |
return "<EnumeratedType '%s'>" % self.name |
|
447 |
||
448 |
||
449 |
class MetaDBEnum(BaseMetaEnum): |
|
450 |
"""The meta class for `DBEnumeratedType`.
|
|
451 |
||
452 |
Provides a method for getting the item based on the database identifier in
|
|
453 |
addition to all the normal enumerated type methods.
|
|
454 |
"""
|
|
455 |
item_type = DBItem |
|
456 |
enum_name = 'DBEnumeratedType' |
|
457 |
||
458 |
@classmethod
|
|
459 |
def _generateItemMapping(cls, items): |
|
460 |
"""DBEnumeratedTypes also map the database value of the DBItem to the
|
|
461 |
item instance."""
|
|
462 |
mapping = BaseMetaEnum._generateItemMapping(items) |
|
463 |
for item_name, item in items: |
|
464 |
# If the value is already in the mapping then we have two
|
|
465 |
# different items attempting to map the same number.
|
|
466 |
if item.value in mapping: |
|
467 |
# We really want to provide the names in alphabetical order.
|
|
468 |
args = [item.value] + sorted( |
|
469 |
[item_name, mapping[item.value].name]) |
|
470 |
raise TypeError( |
|
471 |
'Two DBItems with the same value %s (%s, %s)' |
|
472 |
% tuple(args)) |
|
473 |
else: |
|
474 |
mapping[item.value] = item |
|
475 |
return mapping |
|
476 |
||
477 |
def __repr__(self): |
|
478 |
return "<DBEnumeratedType '%s'>" % self.name |
|
479 |
||
480 |
||
481 |
class EnumeratedType: |
|
482 |
"""An enumeration of items.
|
|
483 |
||
484 |
The items of the enumeration must be instances of the class `Item`.
|
|
485 |
These items are accessible through a class attribute `items`. The ordering
|
|
486 |
of the items attribute is the same order that the items are defined in the
|
|
487 |
class.
|
|
488 |
||
489 |
A `sort_order` attribute can be defined to override the default ordering.
|
|
490 |
The sort_order should contain the names of the all the items in the
|
|
491 |
ordering that is desired.
|
|
492 |
"""
|
|
493 |
__metaclass__ = MetaEnum |
|
494 |
||
495 |
||
496 |
class DBEnumeratedType: |
|
497 |
"""An enumeration with additional mapping from an integer to `Item`.
|
|
498 |
||
499 |
The items of a class inheriting from DBEnumeratedType must be of type
|
|
500 |
`DBItem`.
|
|
501 |
"""
|
|
502 |
__metaclass__ = MetaDBEnum |
|
503 |
||
504 |
||
505 |
def use_template(enum_type, include=None, exclude=None): |
|
506 |
"""An alternative way to extend an enumerated type other than inheritance.
|
|
507 |
||
508 |
The parameters include and exclude should either be the name values of the
|
|
509 |
items (the parameter names), or a list or tuple that contains string
|
|
510 |
values.
|
|
511 |
"""
|
|
512 |
frame = sys._getframe(1) |
|
513 |
locals = frame.f_locals |
|
514 |
||
515 |
# Try to make sure we were called from a class def.
|
|
516 |
if (locals is frame.f_globals) or ('__module__' not in locals): |
|
517 |
raise TypeError( |
|
518 |
"use_template can be used only from a class definition.") |
|
519 |
||
520 |
# You can specify either includes or excludes, not both.
|
|
521 |
if include and exclude: |
|
522 |
raise ValueError("You can specify includes or excludes not both.") |
|
523 |
||
524 |
if include is None: |
|
525 |
items = enum_type.items |
|
526 |
else: |
|
527 |
if isinstance(include, str): |
|
528 |
include = [include] |
|
529 |
items = [item for item in enum_type.items if item.name in include] |
|
530 |
||
531 |
if exclude is None: |
|
532 |
exclude = [] |
|
533 |
elif isinstance(exclude, str): |
|
534 |
exclude = [exclude] |
|
535 |
||
536 |
template_items = [] |
|
537 |
for item in items: |
|
538 |
if item.name not in exclude: |
|
539 |
template_items.append(item) |
|
540 |
||
541 |
locals['template_items'] = template_items |