PrEditor 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of PrEditor might be problematic. Click here for more details.
- preditor/__init__.py +322 -0
- preditor/__main__.py +13 -0
- preditor/about_module.py +161 -0
- preditor/cli.py +192 -0
- preditor/config.py +302 -0
- preditor/contexts.py +119 -0
- preditor/cores/__init__.py +0 -0
- preditor/cores/core.py +20 -0
- preditor/dccs/maya/PrEditor_maya.mod +2 -0
- preditor/dccs/maya/plug-ins/PrEditor_maya.py +110 -0
- preditor/debug.py +144 -0
- preditor/delayable_engine/__init__.py +302 -0
- preditor/delayable_engine/delayables.py +85 -0
- preditor/enum.py +728 -0
- preditor/excepthooks.py +131 -0
- preditor/gui/__init__.py +93 -0
- preditor/gui/app.py +160 -0
- preditor/gui/codehighlighter.py +209 -0
- preditor/gui/completer.py +226 -0
- preditor/gui/console.py +867 -0
- preditor/gui/dialog.py +178 -0
- preditor/gui/drag_tab_bar.py +190 -0
- preditor/gui/editor_chooser.py +57 -0
- preditor/gui/errordialog.py +68 -0
- preditor/gui/find_files.py +125 -0
- preditor/gui/fuzzy_search/__init__.py +0 -0
- preditor/gui/fuzzy_search/fuzzy_search.py +93 -0
- preditor/gui/group_tab_widget/__init__.py +325 -0
- preditor/gui/group_tab_widget/grouped_tab_menu.py +35 -0
- preditor/gui/group_tab_widget/grouped_tab_models.py +108 -0
- preditor/gui/group_tab_widget/grouped_tab_widget.py +78 -0
- preditor/gui/group_tab_widget/one_tab_widget.py +54 -0
- preditor/gui/level_buttons.py +343 -0
- preditor/gui/logger_window_handler.py +48 -0
- preditor/gui/logger_window_plugin.py +32 -0
- preditor/gui/loggerwindow.py +1385 -0
- preditor/gui/newtabwidget.py +69 -0
- preditor/gui/set_text_editor_path_dialog.py +59 -0
- preditor/gui/status_label.py +99 -0
- preditor/gui/suggest_path_quotes_dialog.py +50 -0
- preditor/gui/ui/editor_chooser.ui +93 -0
- preditor/gui/ui/errordialog.ui +74 -0
- preditor/gui/ui/find_files.ui +140 -0
- preditor/gui/ui/loggerwindow.ui +1105 -0
- preditor/gui/ui/set_text_editor_path_dialog.ui +189 -0
- preditor/gui/ui/suggest_path_quotes_dialog.ui +225 -0
- preditor/gui/window.py +161 -0
- preditor/gui/workbox_mixin.py +389 -0
- preditor/gui/workbox_text_edit.py +137 -0
- preditor/gui/workboxwidget.py +298 -0
- preditor/logging_config.py +52 -0
- preditor/osystem.py +401 -0
- preditor/plugins.py +118 -0
- preditor/prefs.py +74 -0
- preditor/resource/environment_variables.html +26 -0
- preditor/resource/error_mail.html +85 -0
- preditor/resource/error_mail_inline.html +41 -0
- preditor/resource/img/README.md +17 -0
- preditor/resource/img/arrow_forward.png +0 -0
- preditor/resource/img/check-bold.png +0 -0
- preditor/resource/img/chevron-down.png +0 -0
- preditor/resource/img/chevron-up.png +0 -0
- preditor/resource/img/close-thick.png +0 -0
- preditor/resource/img/comment-edit.png +0 -0
- preditor/resource/img/content-copy.png +0 -0
- preditor/resource/img/content-cut.png +0 -0
- preditor/resource/img/content-duplicate.png +0 -0
- preditor/resource/img/content-paste.png +0 -0
- preditor/resource/img/content-save.png +0 -0
- preditor/resource/img/debug_disabled.png +0 -0
- preditor/resource/img/eye-check.png +0 -0
- preditor/resource/img/file-plus.png +0 -0
- preditor/resource/img/file-remove.png +0 -0
- preditor/resource/img/format-align-left.png +0 -0
- preditor/resource/img/format-letter-case-lower.png +0 -0
- preditor/resource/img/format-letter-case-upper.png +0 -0
- preditor/resource/img/format-letter-case.svg +1 -0
- preditor/resource/img/information.png +0 -0
- preditor/resource/img/logging_critical.png +0 -0
- preditor/resource/img/logging_custom.png +0 -0
- preditor/resource/img/logging_debug.png +0 -0
- preditor/resource/img/logging_error.png +0 -0
- preditor/resource/img/logging_info.png +0 -0
- preditor/resource/img/logging_not_set.png +0 -0
- preditor/resource/img/logging_warning.png +0 -0
- preditor/resource/img/marker.png +0 -0
- preditor/resource/img/play.png +0 -0
- preditor/resource/img/playlist-play.png +0 -0
- preditor/resource/img/plus-minus-variant.png +0 -0
- preditor/resource/img/preditor.ico +0 -0
- preditor/resource/img/preditor.png +0 -0
- preditor/resource/img/preditor.psd +0 -0
- preditor/resource/img/preditor.svg +44 -0
- preditor/resource/img/regex.svg +1 -0
- preditor/resource/img/restart.svg +1 -0
- preditor/resource/img/skip-forward-outline.png +0 -0
- preditor/resource/img/skip-next-outline.png +0 -0
- preditor/resource/img/skip-next.png +0 -0
- preditor/resource/img/skip-previous.png +0 -0
- preditor/resource/img/subdirectory-arrow-right.png +0 -0
- preditor/resource/img/text-search-variant.png +0 -0
- preditor/resource/img/warning-big.png +0 -0
- preditor/resource/lang/python.json +30 -0
- preditor/resource/settings.ini +25 -0
- preditor/resource/stylesheet/Bright.css +65 -0
- preditor/resource/stylesheet/Dark.css +199 -0
- preditor/scintilla/__init__.py +22 -0
- preditor/scintilla/delayables/__init__.py +11 -0
- preditor/scintilla/delayables/smart_highlight.py +94 -0
- preditor/scintilla/delayables/spell_check.py +173 -0
- preditor/scintilla/documenteditor.py +2038 -0
- preditor/scintilla/finddialog.py +68 -0
- preditor/scintilla/lang/__init__.py +80 -0
- preditor/scintilla/lang/config/bash.ini +15 -0
- preditor/scintilla/lang/config/batch.ini +14 -0
- preditor/scintilla/lang/config/cpp.ini +19 -0
- preditor/scintilla/lang/config/css.ini +19 -0
- preditor/scintilla/lang/config/eyeonscript.ini +17 -0
- preditor/scintilla/lang/config/html.ini +21 -0
- preditor/scintilla/lang/config/javascript.ini +24 -0
- preditor/scintilla/lang/config/lua.ini +16 -0
- preditor/scintilla/lang/config/maxscript.ini +20 -0
- preditor/scintilla/lang/config/mel.ini +18 -0
- preditor/scintilla/lang/config/mu.ini +22 -0
- preditor/scintilla/lang/config/nsi.ini +19 -0
- preditor/scintilla/lang/config/perl.ini +19 -0
- preditor/scintilla/lang/config/puppet.ini +19 -0
- preditor/scintilla/lang/config/python.ini +28 -0
- preditor/scintilla/lang/config/ruby.ini +19 -0
- preditor/scintilla/lang/config/sql.ini +7 -0
- preditor/scintilla/lang/config/xml.ini +21 -0
- preditor/scintilla/lang/config/yaml.ini +18 -0
- preditor/scintilla/lang/language.py +240 -0
- preditor/scintilla/lexers/__init__.py +0 -0
- preditor/scintilla/lexers/cpplexer.py +21 -0
- preditor/scintilla/lexers/javascriptlexer.py +25 -0
- preditor/scintilla/lexers/maxscriptlexer.py +234 -0
- preditor/scintilla/lexers/mellexer.py +368 -0
- preditor/scintilla/lexers/mulexer.py +32 -0
- preditor/scintilla/lexers/pythonlexer.py +41 -0
- preditor/scintilla/ui/finddialog.ui +160 -0
- preditor/settings.py +71 -0
- preditor/stream/__init__.py +80 -0
- preditor/stream/director.py +73 -0
- preditor/stream/manager.py +74 -0
- preditor/streamhandler_helper.py +46 -0
- preditor/utils/__init__.py +0 -0
- preditor/utils/cute.py +30 -0
- preditor/utils/stylesheets.py +54 -0
- preditor/utils/text_search.py +342 -0
- preditor/version.py +21 -0
- preditor/weakref.py +363 -0
- preditor-1.0.0.dist-info/METADATA +224 -0
- preditor-1.0.0.dist-info/RECORD +158 -0
- preditor-1.0.0.dist-info/WHEEL +5 -0
- preditor-1.0.0.dist-info/entry_points.txt +18 -0
- preditor-1.0.0.dist-info/licenses/LICENSE +165 -0
- preditor-1.0.0.dist-info/top_level.txt +1 -0
preditor/enum.py
ADDED
|
@@ -0,0 +1,728 @@
|
|
|
1
|
+
from __future__ import absolute_import
|
|
2
|
+
|
|
3
|
+
import abc
|
|
4
|
+
import re
|
|
5
|
+
from builtins import str as text
|
|
6
|
+
from numbers import Number
|
|
7
|
+
|
|
8
|
+
from future.utils import iteritems, with_metaclass
|
|
9
|
+
from past.builtins import long
|
|
10
|
+
|
|
11
|
+
# =============================================================================
|
|
12
|
+
# CLASSES
|
|
13
|
+
# =============================================================================
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class _MetaEnumGroup(type):
|
|
17
|
+
"""An EnumGroup metaclass."""
|
|
18
|
+
|
|
19
|
+
def __new__(cls, className, bases, classDict): # noqa: B902
|
|
20
|
+
newCls = type.__new__(cls, className, bases, classDict)
|
|
21
|
+
newCls.__init_enums__()
|
|
22
|
+
newCls._cls = cls
|
|
23
|
+
newCls._clsName = className
|
|
24
|
+
newCls._clsBases = bases
|
|
25
|
+
newCls._clsDict = classDict
|
|
26
|
+
return newCls
|
|
27
|
+
|
|
28
|
+
def __call__(cls, number):
|
|
29
|
+
number = int(number)
|
|
30
|
+
e = cls.Nothing
|
|
31
|
+
for enum in cls._ENUMERATORS:
|
|
32
|
+
if enum & number:
|
|
33
|
+
if e:
|
|
34
|
+
e = e | enum
|
|
35
|
+
else:
|
|
36
|
+
e = enum
|
|
37
|
+
return e
|
|
38
|
+
|
|
39
|
+
def __getitem__(cls, key):
|
|
40
|
+
if isinstance(key, Number):
|
|
41
|
+
return list(cls)[int(key)]
|
|
42
|
+
elif isinstance(key, slice):
|
|
43
|
+
# If a Enum is passed convert it to its labelIndex
|
|
44
|
+
start = key.start
|
|
45
|
+
stop = key.stop
|
|
46
|
+
if isinstance(start, Enum):
|
|
47
|
+
start = start.labelIndex
|
|
48
|
+
if isinstance(stop, Enum):
|
|
49
|
+
stop = stop.labelIndex
|
|
50
|
+
return list(cls)[slice(start, stop, key.step)]
|
|
51
|
+
else:
|
|
52
|
+
return getattr(cls, str(key))
|
|
53
|
+
|
|
54
|
+
def __instancecheck__(cls, inst):
|
|
55
|
+
if type(inst) == cls:
|
|
56
|
+
return True
|
|
57
|
+
if isinstance(inst, cls._cls):
|
|
58
|
+
return True
|
|
59
|
+
return False
|
|
60
|
+
|
|
61
|
+
def __iter__(cls):
|
|
62
|
+
for e in cls._ENUMERATORS:
|
|
63
|
+
yield e
|
|
64
|
+
|
|
65
|
+
def __len__(cls):
|
|
66
|
+
return len(cls._ENUMERATORS)
|
|
67
|
+
|
|
68
|
+
def __repr__(cls):
|
|
69
|
+
return '<{mdl}.{cls}({enums})>'.format(
|
|
70
|
+
mdl=cls._clsDict.get('__module__', 'unknown'),
|
|
71
|
+
cls=cls._clsName,
|
|
72
|
+
enums=cls.join(),
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
def __str__(cls):
|
|
76
|
+
return '{0}({1})'.format(cls._clsName, cls.join())
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# =============================================================================
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class Enum(with_metaclass(abc.ABCMeta, object)):
|
|
83
|
+
"""A basic enumerator class.
|
|
84
|
+
|
|
85
|
+
Enumerators are named values that act as identifiers. Typically, a
|
|
86
|
+
list of enumerators are component pieces of an `EnumGroup`.
|
|
87
|
+
|
|
88
|
+
Example::
|
|
89
|
+
|
|
90
|
+
class Suit(Enum):
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
class Suits(EnumGroup):
|
|
94
|
+
Hearts = Suit()
|
|
95
|
+
Spades = Suit()
|
|
96
|
+
Clubs = Suit()
|
|
97
|
+
Diamonds = Suit()
|
|
98
|
+
|
|
99
|
+
Enum objects can be combined and compared using binary "and" and "or"
|
|
100
|
+
operations.
|
|
101
|
+
|
|
102
|
+
Example::
|
|
103
|
+
|
|
104
|
+
mySuits = Suits.Hearts | Suits.Spades
|
|
105
|
+
|
|
106
|
+
if Suits.Hearts & mySuits:
|
|
107
|
+
print("This is true!")
|
|
108
|
+
|
|
109
|
+
if Suits.Clubs & mySuits:
|
|
110
|
+
print("This is false!")
|
|
111
|
+
|
|
112
|
+
Attributes:
|
|
113
|
+
name: The name of the enumerator.
|
|
114
|
+
number: The integer value representation of the enumerator.
|
|
115
|
+
label: The enumerator's label.
|
|
116
|
+
labelIndex: The enumerator's index within its parent EnumGroup.
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
_CREATIONORDER = 0
|
|
120
|
+
|
|
121
|
+
def __init__(self, number=None, label=None, **kwargs):
|
|
122
|
+
"""Initializes a new Enum object.
|
|
123
|
+
|
|
124
|
+
In addition to the named arguments listed below, keyword arguments
|
|
125
|
+
may be given that will be set as attributes on the Enum.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
number(int): The integer representation of the Enum. The default
|
|
129
|
+
is to have this number determined dynamically based on its
|
|
130
|
+
place with the parent EnumGroup.
|
|
131
|
+
label(str): The Enum's label. The default is to inherit the
|
|
132
|
+
attribute name the Enum is associated with in its parent
|
|
133
|
+
EnumGroup.
|
|
134
|
+
"""
|
|
135
|
+
self._creationOrder = Enum._CREATIONORDER
|
|
136
|
+
Enum._CREATIONORDER += 1
|
|
137
|
+
self._name = None
|
|
138
|
+
self._number = number
|
|
139
|
+
self._label = label
|
|
140
|
+
self._labelIndex = None
|
|
141
|
+
self._cmpLabel = None
|
|
142
|
+
self._cmpName = None
|
|
143
|
+
self._enumGroup = None
|
|
144
|
+
if kwargs:
|
|
145
|
+
self.__dict__.update(kwargs)
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def name(self):
|
|
149
|
+
"""The name of the Enum."""
|
|
150
|
+
return self._name
|
|
151
|
+
|
|
152
|
+
@property
|
|
153
|
+
def number(self):
|
|
154
|
+
"""The number representation of the Enum."""
|
|
155
|
+
return self._number
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def label(self):
|
|
159
|
+
"""The Enum's label."""
|
|
160
|
+
return self._label
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def labelIndex(self):
|
|
164
|
+
"""The Enum's index within its parent EnumGroup."""
|
|
165
|
+
return self._labelIndex
|
|
166
|
+
|
|
167
|
+
def _setName(self, name):
|
|
168
|
+
if name is None:
|
|
169
|
+
self._name = None
|
|
170
|
+
self._cmpName = None
|
|
171
|
+
else:
|
|
172
|
+
self._name = name
|
|
173
|
+
self._cmpName = name.strip('_ ')
|
|
174
|
+
|
|
175
|
+
def _setLabel(self, label):
|
|
176
|
+
if label is None:
|
|
177
|
+
self._label = None
|
|
178
|
+
self._cmpLabel = None
|
|
179
|
+
else:
|
|
180
|
+
self._label = label
|
|
181
|
+
self._cmpLabel = label.replace(' ', '').replace('_', '')
|
|
182
|
+
|
|
183
|
+
def __add__(self, other):
|
|
184
|
+
return self.__or__(other)
|
|
185
|
+
|
|
186
|
+
def __and__(self, other):
|
|
187
|
+
if isinstance(other, Enum):
|
|
188
|
+
other = int(other)
|
|
189
|
+
return int(self) & other
|
|
190
|
+
|
|
191
|
+
def __call__(self):
|
|
192
|
+
return int(self)
|
|
193
|
+
|
|
194
|
+
def __cmp__(self, value):
|
|
195
|
+
if not isinstance(value, Enum):
|
|
196
|
+
return -1
|
|
197
|
+
return self.number - value.number
|
|
198
|
+
|
|
199
|
+
def __lt__(self, value):
|
|
200
|
+
return self.__cmp__(value) < 0
|
|
201
|
+
|
|
202
|
+
def __eq__(self, value):
|
|
203
|
+
if value is None:
|
|
204
|
+
return False
|
|
205
|
+
if isinstance(value, Enum):
|
|
206
|
+
return self.number == value.number
|
|
207
|
+
if isinstance(value, (int, long)):
|
|
208
|
+
return self.number == value
|
|
209
|
+
if isinstance(value, str) or isinstance(value, text):
|
|
210
|
+
if self._compareStr(value):
|
|
211
|
+
return True
|
|
212
|
+
return False
|
|
213
|
+
|
|
214
|
+
def __hash__(self):
|
|
215
|
+
return self.number
|
|
216
|
+
|
|
217
|
+
def __index__(self):
|
|
218
|
+
return self.number
|
|
219
|
+
|
|
220
|
+
def __int__(self):
|
|
221
|
+
return self.number or 0
|
|
222
|
+
|
|
223
|
+
def __invert__(self):
|
|
224
|
+
return ~int(self)
|
|
225
|
+
|
|
226
|
+
def __ne__(self, value):
|
|
227
|
+
return not self.__eq__(value)
|
|
228
|
+
|
|
229
|
+
def __nonzero__(self):
|
|
230
|
+
return bool(int(self))
|
|
231
|
+
|
|
232
|
+
def __or__(self, other):
|
|
233
|
+
o = other
|
|
234
|
+
if isinstance(other, Enum):
|
|
235
|
+
o = int(other)
|
|
236
|
+
# No need to return a CompositeEnum if one of these is Nothing
|
|
237
|
+
if o == 0:
|
|
238
|
+
return self
|
|
239
|
+
v = int(self)
|
|
240
|
+
if v == 0:
|
|
241
|
+
return other
|
|
242
|
+
value = v | o
|
|
243
|
+
label = '{0} {1}'.format(str(self), str(other))
|
|
244
|
+
name = '{0}_{1}'.format(str(self), str(other))
|
|
245
|
+
|
|
246
|
+
class CompositeEnum(Enum):
|
|
247
|
+
def __init__(ss, number, lbl, name): # noqa: N805,B902
|
|
248
|
+
super(CompositeEnum, ss).__init__(number, lbl)
|
|
249
|
+
ss._name = name
|
|
250
|
+
|
|
251
|
+
# Register our composite enum class as a virtual
|
|
252
|
+
# subclass of this enum's class, plus the same for
|
|
253
|
+
# the other enum if it's an Enum object. This
|
|
254
|
+
# will make the composite enum isinstance check true
|
|
255
|
+
# against both.
|
|
256
|
+
type(self).register(CompositeEnum)
|
|
257
|
+
if isinstance(other, Enum):
|
|
258
|
+
type(other).register(CompositeEnum)
|
|
259
|
+
return CompositeEnum(value, label, name)
|
|
260
|
+
|
|
261
|
+
def __rand__(self, other):
|
|
262
|
+
if isinstance(other, Enum):
|
|
263
|
+
other = int(other)
|
|
264
|
+
return other & int(self)
|
|
265
|
+
|
|
266
|
+
def __repr__(self):
|
|
267
|
+
return '<{mdl}.{cls}.{name}>'.format(
|
|
268
|
+
mdl=self.__class__.__module__,
|
|
269
|
+
cls=self.__class__.__name__,
|
|
270
|
+
name=str(self.name),
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
def __ror__(self, other):
|
|
274
|
+
return self | other
|
|
275
|
+
|
|
276
|
+
def __rxor__(self, other):
|
|
277
|
+
return self ^ other
|
|
278
|
+
|
|
279
|
+
def __str__(self):
|
|
280
|
+
if self.name:
|
|
281
|
+
return self.name
|
|
282
|
+
return self.label or ''
|
|
283
|
+
|
|
284
|
+
def __xor__(self, other):
|
|
285
|
+
if isinstance(other, Enum):
|
|
286
|
+
other = int(other)
|
|
287
|
+
return int(self) ^ other
|
|
288
|
+
|
|
289
|
+
def _compareStr(self, inStr):
|
|
290
|
+
return inStr.replace(' ', '').replace('_', '') in (
|
|
291
|
+
self._cmpLabel,
|
|
292
|
+
self._cmpName,
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
# =============================================================================
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
class EnumGroup(with_metaclass(_MetaEnumGroup, object)):
|
|
300
|
+
"""A container class for collecting, organizing, and accessing Enums.
|
|
301
|
+
|
|
302
|
+
An EnumGroup class is a container for Enum objects. It provides
|
|
303
|
+
organizational convenience, and in most cases handles the generation
|
|
304
|
+
and assignment of Enum numbers, names, and labels.
|
|
305
|
+
|
|
306
|
+
Example::
|
|
307
|
+
|
|
308
|
+
class Suit(Enum):
|
|
309
|
+
pass
|
|
310
|
+
|
|
311
|
+
class Suits(EnumGroup):
|
|
312
|
+
Hearts = Suit()
|
|
313
|
+
Spades = Suit()
|
|
314
|
+
Clubs = Suit()
|
|
315
|
+
Diamonds = Suit()
|
|
316
|
+
|
|
317
|
+
The above example outlines defining an enumerator, and grouping
|
|
318
|
+
four of them inside of a group. This provides a number of things,
|
|
319
|
+
including references by attribute, name, and index. Also provided
|
|
320
|
+
is an "All" attribute, if one is not explicitly assigned, it will be
|
|
321
|
+
a CompositeEnum of all the defined enums, and compare true against
|
|
322
|
+
any members of the group via the binary "and" operator. Also provided
|
|
323
|
+
is an "Nothing" attribute, if one is not explicitly assigned, it
|
|
324
|
+
compares false against any members of the group and when converted to
|
|
325
|
+
a int its value will be zero.
|
|
326
|
+
|
|
327
|
+
Example::
|
|
328
|
+
|
|
329
|
+
# By attribute.
|
|
330
|
+
Suits.Hearts
|
|
331
|
+
|
|
332
|
+
# By name.
|
|
333
|
+
Suits['Hearts']
|
|
334
|
+
|
|
335
|
+
suitList = list(Suits)
|
|
336
|
+
|
|
337
|
+
if Suits.Hearts & Suits.All:
|
|
338
|
+
print("This is true!")
|
|
339
|
+
|
|
340
|
+
You can also pass a int value as a index lookup. If you pass a int value it
|
|
341
|
+
will return the object by its index. This means you can not lookup composite
|
|
342
|
+
Enum objects as 3 returns the third index which in the above example is Diamonds.
|
|
343
|
+
The index value for Enum is stored on its labelIndex property.
|
|
344
|
+
|
|
345
|
+
Example::
|
|
346
|
+
|
|
347
|
+
print(Suits.Diamonds.labelIndex)
|
|
348
|
+
|
|
349
|
+
if Suits.Diamonds == Suits[3]:
|
|
350
|
+
print("This is true!")
|
|
351
|
+
|
|
352
|
+
An EnumGroup can be sliced by passing an Enum object or its labelIndex value for
|
|
353
|
+
start and stop, returning a list of matching Enums.
|
|
354
|
+
|
|
355
|
+
Example::
|
|
356
|
+
|
|
357
|
+
# All Enums between Spades and Diamonds
|
|
358
|
+
Suits[Suits.Spades:Suits.Diamonds]
|
|
359
|
+
|
|
360
|
+
# All Enums between Spades and Clubs including Clubs
|
|
361
|
+
Suits[Suits.Spades:Suits.Clubs.labelIndex+1]
|
|
362
|
+
|
|
363
|
+
An EnumGroup can also act as a factory for composite Enum objects.
|
|
364
|
+
If a known composite value is available, like 3, which is the
|
|
365
|
+
combination of enum values 1 and 2, a composite Enum object can
|
|
366
|
+
be constructed.
|
|
367
|
+
|
|
368
|
+
Example::
|
|
369
|
+
|
|
370
|
+
comp = Suits(3)
|
|
371
|
+
|
|
372
|
+
if Suits.Hearts & comp:
|
|
373
|
+
print("This is true!")
|
|
374
|
+
|
|
375
|
+
if Suits.Clubs & comp:
|
|
376
|
+
print("This is false!")
|
|
377
|
+
|
|
378
|
+
If one of the Enum's has the default keyword argument set to True, then that Enum
|
|
379
|
+
is also exposed as the "Default" attribute. Additionally all other Enum's will have
|
|
380
|
+
a default property added and set to False.
|
|
381
|
+
|
|
382
|
+
Example::
|
|
383
|
+
|
|
384
|
+
class Suits(EnumGroup):
|
|
385
|
+
Hearts = Suit()
|
|
386
|
+
Spades = Suit(default=True)
|
|
387
|
+
|
|
388
|
+
assert Suits.Hearts.default == False
|
|
389
|
+
assert Suits.Spades.default == True
|
|
390
|
+
assert Suits.Default == Suits.Spades
|
|
391
|
+
|
|
392
|
+
Attributes:
|
|
393
|
+
All: The sum of all members.
|
|
394
|
+
Nothing: None of the members.
|
|
395
|
+
"""
|
|
396
|
+
|
|
397
|
+
_ENUMERATORS = None
|
|
398
|
+
_copyCount = 1
|
|
399
|
+
All = 0
|
|
400
|
+
Nothing = 0
|
|
401
|
+
|
|
402
|
+
def __init__(self):
|
|
403
|
+
raise InstantiationError('Unable to instantiate static class EnumGroup.')
|
|
404
|
+
|
|
405
|
+
@classmethod
|
|
406
|
+
def append(cls, *args, **kwargs):
|
|
407
|
+
"""Appends additional enumerators to the EnumGroup.
|
|
408
|
+
|
|
409
|
+
New members can be provided as ordered arguments where the
|
|
410
|
+
each Enum's label is used to determine the attribute name, or
|
|
411
|
+
by keyword arguments where the key is the attribute name and
|
|
412
|
+
the Enum is the value. When using an Enum's label to determine
|
|
413
|
+
its name, any spaces in the label will be converted to underscores.
|
|
414
|
+
|
|
415
|
+
Example:
|
|
416
|
+
Suits.append(Suit(None, 'Funky'), Foo=Suit())
|
|
417
|
+
|
|
418
|
+
# The "Funky" and "Foo" suits are now available.
|
|
419
|
+
Suits.Funky
|
|
420
|
+
Suits.Foo
|
|
421
|
+
|
|
422
|
+
Raises:
|
|
423
|
+
ValueError
|
|
424
|
+
"""
|
|
425
|
+
if [e for e in (list(args) + list(kwargs.values())) if not isinstance(e, Enum)]:
|
|
426
|
+
raise ValueError('Given items must be of class Enum.')
|
|
427
|
+
if [e for e in args if not e.label]:
|
|
428
|
+
raise ValueError('Enums given as ordered arguments must have a label.')
|
|
429
|
+
for e in args:
|
|
430
|
+
setattr(cls, cls._labelToVarName(e.label), e)
|
|
431
|
+
for n, e in iteritems(kwargs):
|
|
432
|
+
setattr(cls, n, e)
|
|
433
|
+
# reset All and Nothing -- this is necessary so that All is regenerated
|
|
434
|
+
# and so that Nothing is not included when finding the member Enums.
|
|
435
|
+
cls.All = 0
|
|
436
|
+
cls.Nothing = 0
|
|
437
|
+
cls.__init_enums__()
|
|
438
|
+
|
|
439
|
+
@classmethod
|
|
440
|
+
def copy(cls, name=None):
|
|
441
|
+
"""Returns a new class type from this class without any Enums assigned.
|
|
442
|
+
|
|
443
|
+
If name is not provided it will automatically generate a new class name.
|
|
444
|
+
For example if the EnumGroup class named DefaultEnums has been copied
|
|
445
|
+
twice the new class name will be "DefaultEnums_3".
|
|
446
|
+
If you provide name it will not check for duplicates.
|
|
447
|
+
|
|
448
|
+
Args:
|
|
449
|
+
name (str|None): The name to give the new class. Defaults to None
|
|
450
|
+
|
|
451
|
+
Returns:
|
|
452
|
+
EnumGroup: A new Class type.
|
|
453
|
+
"""
|
|
454
|
+
if not name:
|
|
455
|
+
# Generate a unique name for the class if one was not provided
|
|
456
|
+
name = '{name}_{count}'.format(name=cls.__name__, count=cls._copyCount)
|
|
457
|
+
cls._copyCount += 1
|
|
458
|
+
return type(name, cls.__bases__, dict(cls.__dict__))
|
|
459
|
+
|
|
460
|
+
@classmethod
|
|
461
|
+
def fromLabel(cls, label, default=None):
|
|
462
|
+
"""Gets an enumerator based on the given label.
|
|
463
|
+
|
|
464
|
+
If a default is provided and is not None, that value will be returned
|
|
465
|
+
in the event that the given label does not exist in the EnumGroup. If
|
|
466
|
+
no default is provided, a ValueError is raised.
|
|
467
|
+
|
|
468
|
+
Args:
|
|
469
|
+
label(str): The label to look up.
|
|
470
|
+
default(*): The default value to return if the label is not found.
|
|
471
|
+
|
|
472
|
+
Raises:
|
|
473
|
+
ValueError: Raised if default is None and the given label does not
|
|
474
|
+
exist in the EnumGroup.
|
|
475
|
+
|
|
476
|
+
Returns:
|
|
477
|
+
Enum
|
|
478
|
+
"""
|
|
479
|
+
label = str(label)
|
|
480
|
+
for e in cls._ENUMERATORS:
|
|
481
|
+
if e.label == label:
|
|
482
|
+
return e
|
|
483
|
+
if default is not None:
|
|
484
|
+
return default
|
|
485
|
+
raise ValueError('No enumerators exist with the given label.')
|
|
486
|
+
|
|
487
|
+
@classmethod
|
|
488
|
+
def fromValue(cls, value, default=None, allowComposite=False):
|
|
489
|
+
"""Gets an enumerator based on the given value.
|
|
490
|
+
|
|
491
|
+
If a default is provided and is not None, that value will be returned
|
|
492
|
+
in the event that the given label does not exist in the EnumGroup. If
|
|
493
|
+
no default is provided, a ValueError is raised.
|
|
494
|
+
|
|
495
|
+
Args:
|
|
496
|
+
value (int): The value to look up.
|
|
497
|
+
default (*): The default value to return if the label is not found.
|
|
498
|
+
allowComposite (bool, optional): If True a composite enums will be
|
|
499
|
+
created when provided a value that is the sum of multiple enum
|
|
500
|
+
values. Otherwise, a ValueError will be raised. Defaults to
|
|
501
|
+
False.
|
|
502
|
+
|
|
503
|
+
Returns:
|
|
504
|
+
Enum
|
|
505
|
+
|
|
506
|
+
Raises:
|
|
507
|
+
ValueError: Raised if default is None and the given label does not
|
|
508
|
+
exist in the EnumGroup.
|
|
509
|
+
"""
|
|
510
|
+
value = int(value)
|
|
511
|
+
composite = None
|
|
512
|
+
for e in cls._ENUMERATORS:
|
|
513
|
+
eVal = int(e)
|
|
514
|
+
if eVal == value:
|
|
515
|
+
return e
|
|
516
|
+
if allowComposite and eVal & value:
|
|
517
|
+
composite = e if composite is None else (composite | e)
|
|
518
|
+
if composite is not None and int(composite) == value:
|
|
519
|
+
return composite
|
|
520
|
+
if default is not None:
|
|
521
|
+
return default
|
|
522
|
+
raise ValueError('No enumerators exist with the given value.')
|
|
523
|
+
|
|
524
|
+
@classmethod
|
|
525
|
+
def join(cls, include=None, separator=','):
|
|
526
|
+
"""Joins all child Enums together into a single string.
|
|
527
|
+
|
|
528
|
+
The string representation of each Enum is joined using the
|
|
529
|
+
given separator.
|
|
530
|
+
|
|
531
|
+
Args:
|
|
532
|
+
include(int|Enum): Only enumerators that compare via bitwise "and" against
|
|
533
|
+
the given int or Enum will be returned. Default is EnumGroup.All.
|
|
534
|
+
separator(str): The separator to use. Default is ",".
|
|
535
|
+
|
|
536
|
+
Returns:
|
|
537
|
+
str: The joined enumerators.
|
|
538
|
+
"""
|
|
539
|
+
include = include is None and cls.All or include
|
|
540
|
+
return str(separator).join(
|
|
541
|
+
[str(e) for e in cls._ENUMERATORS if e & int(include)]
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
@classmethod
|
|
545
|
+
def labels(cls):
|
|
546
|
+
"""A generator containing all Enum labels in the EnumGroup."""
|
|
547
|
+
return (e.label for e in cls._ENUMERATORS)
|
|
548
|
+
|
|
549
|
+
@classmethod
|
|
550
|
+
def names(cls):
|
|
551
|
+
"""A generator containing all Enum names in the EnumGroup."""
|
|
552
|
+
return (e.name for e in cls._ENUMERATORS)
|
|
553
|
+
|
|
554
|
+
@classmethod
|
|
555
|
+
def split(cls, string, separator=','):
|
|
556
|
+
"""Splits the given string and returns the corresponding Enums.
|
|
557
|
+
|
|
558
|
+
The string is split using the provided separator, and all names
|
|
559
|
+
contained within must be attributes of the EnumGroup class that
|
|
560
|
+
is performing the split.
|
|
561
|
+
|
|
562
|
+
Args:
|
|
563
|
+
string(str): The string containing the desired Enum names.
|
|
564
|
+
separator(str): The separator to split on. Default is ','.
|
|
565
|
+
|
|
566
|
+
Raises:
|
|
567
|
+
AttributeError
|
|
568
|
+
|
|
569
|
+
Returns:
|
|
570
|
+
list(Enum, ...): The list of resulting Enum objects.
|
|
571
|
+
"""
|
|
572
|
+
names = str(string).split(str(separator))
|
|
573
|
+
return [getattr(cls, n) for n in names]
|
|
574
|
+
|
|
575
|
+
@classmethod
|
|
576
|
+
def values(cls):
|
|
577
|
+
"""A generator containing all Enum values in the EnumGroup."""
|
|
578
|
+
return (int(e) for e in cls._ENUMERATORS)
|
|
579
|
+
|
|
580
|
+
@classmethod
|
|
581
|
+
def __init_enums__(cls):
|
|
582
|
+
enums = []
|
|
583
|
+
default_enum = None
|
|
584
|
+
|
|
585
|
+
orderedEnums = sorted(
|
|
586
|
+
[
|
|
587
|
+
(k, v)
|
|
588
|
+
for k, v in iteritems(cls.__dict__)
|
|
589
|
+
if isinstance(v, Enum) and k not in ('All', 'Nothing')
|
|
590
|
+
],
|
|
591
|
+
key=lambda i: i[1]._creationOrder,
|
|
592
|
+
)
|
|
593
|
+
for name, value in orderedEnums:
|
|
594
|
+
enums.append(value)
|
|
595
|
+
value._enumGroup = cls
|
|
596
|
+
value._setName(name)
|
|
597
|
+
if value.label is None:
|
|
598
|
+
value._setLabel(cls._varNameToLabel(name))
|
|
599
|
+
# Check for a default property and raise a error if more than one is found
|
|
600
|
+
if hasattr(value, 'default') and value.default:
|
|
601
|
+
if default_enum is not None:
|
|
602
|
+
raise ValueError(
|
|
603
|
+
(
|
|
604
|
+
'"{}" already defines default and "{}" is trying to '
|
|
605
|
+
'claim the default'
|
|
606
|
+
).format(default_enum, value)
|
|
607
|
+
)
|
|
608
|
+
default_enum = value
|
|
609
|
+
|
|
610
|
+
enumNumbers = [enum.number for enum in enums if enum.number]
|
|
611
|
+
num = 1
|
|
612
|
+
for enum in enums:
|
|
613
|
+
if enum._number is None:
|
|
614
|
+
while num in enumNumbers:
|
|
615
|
+
num *= 2
|
|
616
|
+
enum._number = num
|
|
617
|
+
enumNumbers.append(num)
|
|
618
|
+
enums.sort()
|
|
619
|
+
labelIndex = 0
|
|
620
|
+
for enum in enums:
|
|
621
|
+
if enum._label is not None:
|
|
622
|
+
enum._labelIndex = labelIndex
|
|
623
|
+
labelIndex += 1
|
|
624
|
+
# Add a default property to all enums for consistency
|
|
625
|
+
if default_enum and not hasattr(enum, 'default'):
|
|
626
|
+
enum.default = False
|
|
627
|
+
cls._ENUMERATORS = enums
|
|
628
|
+
# Build the All object if its not defined
|
|
629
|
+
if isinstance(cls.All, int):
|
|
630
|
+
for e in enums:
|
|
631
|
+
if isinstance(cls.All, int):
|
|
632
|
+
cls.All = e
|
|
633
|
+
else:
|
|
634
|
+
cls.All |= e
|
|
635
|
+
# Build the Nothing object if its not defined
|
|
636
|
+
if isinstance(cls.Nothing, int) and enums:
|
|
637
|
+
processed = set()
|
|
638
|
+
for i, enum in enumerate(enums):
|
|
639
|
+
enumClass = enum.__class__
|
|
640
|
+
if i == 0:
|
|
641
|
+
# Create the Nothing instance from the first class type
|
|
642
|
+
cls.Nothing = enumClass(0, 'Nothing')
|
|
643
|
+
elif enumClass not in processed:
|
|
644
|
+
# Register our Nothing enum's class as a virtual
|
|
645
|
+
# subclass of any additional enum classes. This
|
|
646
|
+
# will make the Nothing enum isinstance check true
|
|
647
|
+
# against all Enums in this EnumGroup.
|
|
648
|
+
enumClass.register(cls.Nothing.__class__)
|
|
649
|
+
processed.add(enumClass)
|
|
650
|
+
# If a default was specified, store it on the Default argument
|
|
651
|
+
if default_enum and not hasattr(cls, 'Default'):
|
|
652
|
+
cls.Default = default_enum
|
|
653
|
+
# Ensure the All and Nothing Enum's have a default as well
|
|
654
|
+
cls.All.default = False
|
|
655
|
+
if enums:
|
|
656
|
+
cls.Nothing.default = False
|
|
657
|
+
|
|
658
|
+
@classmethod
|
|
659
|
+
def _varNameToLabel(cls, varName):
|
|
660
|
+
label = str(varName)
|
|
661
|
+
label = ' '.join(re.findall('[A-Z]+[^A-Z]*', label))
|
|
662
|
+
label = re.sub(r'[_\s]+', ' ', label)
|
|
663
|
+
return label
|
|
664
|
+
|
|
665
|
+
@classmethod
|
|
666
|
+
def _labelToVarName(cls, label):
|
|
667
|
+
name = str(label)
|
|
668
|
+
name = re.sub(r'\s+', '_', name)
|
|
669
|
+
return name
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
# =============================================================================
|
|
673
|
+
class Incrementer(object):
|
|
674
|
+
"""A class that behaves similarly to c i++ or ++i.
|
|
675
|
+
|
|
676
|
+
Once you init this class, every time you call it it will update count and return the
|
|
677
|
+
previous value like c's i++. If you pass True to pre, it will increment then return
|
|
678
|
+
the new value like c's ++i.
|
|
679
|
+
|
|
680
|
+
Args:
|
|
681
|
+
start (int): Start the counter at this value. Defaults to Zero.
|
|
682
|
+
|
|
683
|
+
increment (int): increment by this value. In most cases it should be 1 or -1.
|
|
684
|
+
Defaults to one.
|
|
685
|
+
|
|
686
|
+
pre (bool): If true calling the object will return the incremented value. If
|
|
687
|
+
False it will return the current value and increment for the next call.
|
|
688
|
+
Defaults to False.
|
|
689
|
+
|
|
690
|
+
Attributes:
|
|
691
|
+
count: The current value.
|
|
692
|
+
increment: The incremnt added to count
|
|
693
|
+
pre: Should it preform a ++i or i++ operation when called.
|
|
694
|
+
"""
|
|
695
|
+
|
|
696
|
+
def __init__(self, start=0, increment=1, pre=False):
|
|
697
|
+
super(Incrementer, self).__init__()
|
|
698
|
+
self.count = start
|
|
699
|
+
self.increment = increment
|
|
700
|
+
self.pre = pre
|
|
701
|
+
|
|
702
|
+
def __call__(self):
|
|
703
|
+
if self.pre:
|
|
704
|
+
self.count += self.increment
|
|
705
|
+
return self.count
|
|
706
|
+
ret = self.count
|
|
707
|
+
self.count += self.increment
|
|
708
|
+
return ret
|
|
709
|
+
|
|
710
|
+
def __repr__(self):
|
|
711
|
+
return '{}.{}(start={}, increment={}, pre={!r})'.format(
|
|
712
|
+
self.__module__, type(self).__name__, self.count, self.increment, self.pre
|
|
713
|
+
)
|
|
714
|
+
|
|
715
|
+
def __str__(self):
|
|
716
|
+
return str(self.count)
|
|
717
|
+
|
|
718
|
+
|
|
719
|
+
# =============================================================================
|
|
720
|
+
# EXCEPTIONS
|
|
721
|
+
# =============================================================================
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
class InstantiationError(Exception):
|
|
725
|
+
pass
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
# =============================================================================
|