PrEditor 2.1.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.
Files changed (179) hide show
  1. preditor/__init__.py +315 -0
  2. preditor/__main__.py +13 -0
  3. preditor/about_module.py +165 -0
  4. preditor/cli.py +192 -0
  5. preditor/config.py +318 -0
  6. preditor/constants.py +13 -0
  7. preditor/contexts.py +210 -0
  8. preditor/cores/__init__.py +0 -0
  9. preditor/cores/core.py +20 -0
  10. preditor/dccs/.hab.json +10 -0
  11. preditor/dccs/maya/PrEditor_maya.mod +1 -0
  12. preditor/dccs/maya/README.md +22 -0
  13. preditor/dccs/maya/plug-ins/PrEditor_maya.py +141 -0
  14. preditor/dccs/studiomax/PackageContents.xml +32 -0
  15. preditor/dccs/studiomax/PrEditor-PrEditor_Show.mcr +8 -0
  16. preditor/dccs/studiomax/README.md +17 -0
  17. preditor/dccs/studiomax/preditor.ms +16 -0
  18. preditor/dccs/studiomax/preditor_menu.mnx +7 -0
  19. preditor/debug.py +149 -0
  20. preditor/delayable_engine/__init__.py +302 -0
  21. preditor/delayable_engine/delayables.py +85 -0
  22. preditor/enum.py +728 -0
  23. preditor/excepthooks.py +165 -0
  24. preditor/gui/__init__.py +56 -0
  25. preditor/gui/app.py +163 -0
  26. preditor/gui/codehighlighter.py +289 -0
  27. preditor/gui/completer.py +237 -0
  28. preditor/gui/console.py +605 -0
  29. preditor/gui/console_base.py +911 -0
  30. preditor/gui/dialog.py +181 -0
  31. preditor/gui/drag_tab_bar.py +625 -0
  32. preditor/gui/editor_chooser.py +57 -0
  33. preditor/gui/errordialog.py +69 -0
  34. preditor/gui/find_files.py +137 -0
  35. preditor/gui/fuzzy_search/__init__.py +0 -0
  36. preditor/gui/fuzzy_search/fuzzy_search.py +97 -0
  37. preditor/gui/group_tab_widget/__init__.py +0 -0
  38. preditor/gui/group_tab_widget/group_tab_widget.py +528 -0
  39. preditor/gui/group_tab_widget/grouped_tab_menu.py +35 -0
  40. preditor/gui/group_tab_widget/grouped_tab_models.py +107 -0
  41. preditor/gui/group_tab_widget/grouped_tab_widget.py +223 -0
  42. preditor/gui/group_tab_widget/one_tab_widget.py +96 -0
  43. preditor/gui/level_buttons.py +358 -0
  44. preditor/gui/logger_window_handler.py +77 -0
  45. preditor/gui/logger_window_plugin.py +35 -0
  46. preditor/gui/loggerwindow.py +2405 -0
  47. preditor/gui/newtabwidget.py +69 -0
  48. preditor/gui/output_console.py +11 -0
  49. preditor/gui/qtdesigner/__init__.py +21 -0
  50. preditor/gui/qtdesigner/_log_plugin.py +29 -0
  51. preditor/gui/qtdesigner/console_base_plugin.py +48 -0
  52. preditor/gui/qtdesigner/console_predit_plugin.py +48 -0
  53. preditor/gui/set_text_editor_path_dialog.py +61 -0
  54. preditor/gui/status_label.py +99 -0
  55. preditor/gui/suggest_path_quotes_dialog.py +50 -0
  56. preditor/gui/ui/editor_chooser.ui +93 -0
  57. preditor/gui/ui/errordialog.ui +74 -0
  58. preditor/gui/ui/find_files.ui +140 -0
  59. preditor/gui/ui/loggerwindow.ui +1909 -0
  60. preditor/gui/ui/set_text_editor_path_dialog.ui +189 -0
  61. preditor/gui/ui/suggest_path_quotes_dialog.ui +225 -0
  62. preditor/gui/window.py +161 -0
  63. preditor/gui/workbox_mixin.py +1139 -0
  64. preditor/gui/workbox_text_edit.py +136 -0
  65. preditor/gui/workboxwidget.py +315 -0
  66. preditor/logging_config.py +55 -0
  67. preditor/osystem.py +401 -0
  68. preditor/plugins.py +118 -0
  69. preditor/prefs.py +381 -0
  70. preditor/resource/environment_variables.html +26 -0
  71. preditor/resource/error_mail.html +85 -0
  72. preditor/resource/error_mail_inline.html +41 -0
  73. preditor/resource/img/README.md +17 -0
  74. preditor/resource/img/arrow_forward.png +0 -0
  75. preditor/resource/img/check-bold.png +0 -0
  76. preditor/resource/img/chevron-down.png +0 -0
  77. preditor/resource/img/chevron-up.png +0 -0
  78. preditor/resource/img/close-thick.png +0 -0
  79. preditor/resource/img/comment-edit.png +0 -0
  80. preditor/resource/img/content-copy.png +0 -0
  81. preditor/resource/img/content-cut.png +0 -0
  82. preditor/resource/img/content-duplicate.png +0 -0
  83. preditor/resource/img/content-paste.png +0 -0
  84. preditor/resource/img/content-save.png +0 -0
  85. preditor/resource/img/debug_disabled.png +0 -0
  86. preditor/resource/img/eye-check.png +0 -0
  87. preditor/resource/img/file-plus.png +0 -0
  88. preditor/resource/img/file-remove.png +0 -0
  89. preditor/resource/img/format-align-left.png +0 -0
  90. preditor/resource/img/format-letter-case-lower.png +0 -0
  91. preditor/resource/img/format-letter-case-upper.png +0 -0
  92. preditor/resource/img/format-letter-case.svg +1 -0
  93. preditor/resource/img/information.png +0 -0
  94. preditor/resource/img/logging_critical.png +0 -0
  95. preditor/resource/img/logging_custom.png +0 -0
  96. preditor/resource/img/logging_debug.png +0 -0
  97. preditor/resource/img/logging_error.png +0 -0
  98. preditor/resource/img/logging_info.png +0 -0
  99. preditor/resource/img/logging_not_set.png +0 -0
  100. preditor/resource/img/logging_warning.png +0 -0
  101. preditor/resource/img/marker.png +0 -0
  102. preditor/resource/img/play.png +0 -0
  103. preditor/resource/img/playlist-play.png +0 -0
  104. preditor/resource/img/plus-minus-variant.png +0 -0
  105. preditor/resource/img/preditor.ico +0 -0
  106. preditor/resource/img/preditor.png +0 -0
  107. preditor/resource/img/preditor.psd +0 -0
  108. preditor/resource/img/preditor.svg +44 -0
  109. preditor/resource/img/regex.svg +1 -0
  110. preditor/resource/img/restart.svg +1 -0
  111. preditor/resource/img/skip-forward-outline.png +0 -0
  112. preditor/resource/img/skip-next-outline.png +0 -0
  113. preditor/resource/img/skip-next.png +0 -0
  114. preditor/resource/img/skip-previous.png +0 -0
  115. preditor/resource/img/subdirectory-arrow-right.png +0 -0
  116. preditor/resource/img/text-search-variant.png +0 -0
  117. preditor/resource/img/warning-big.png +0 -0
  118. preditor/resource/lang/python.json +30 -0
  119. preditor/resource/pref_updates/pref_updates.json +17 -0
  120. preditor/resource/settings.ini +25 -0
  121. preditor/resource/stylesheet/Bright.css +76 -0
  122. preditor/resource/stylesheet/Dark.css +210 -0
  123. preditor/scintilla/__init__.py +40 -0
  124. preditor/scintilla/delayables/__init__.py +11 -0
  125. preditor/scintilla/delayables/smart_highlight.py +97 -0
  126. preditor/scintilla/delayables/spell_check.py +174 -0
  127. preditor/scintilla/documenteditor.py +1924 -0
  128. preditor/scintilla/finddialog.py +68 -0
  129. preditor/scintilla/lang/__init__.py +80 -0
  130. preditor/scintilla/lang/config/bash.ini +15 -0
  131. preditor/scintilla/lang/config/batch.ini +14 -0
  132. preditor/scintilla/lang/config/cpp.ini +19 -0
  133. preditor/scintilla/lang/config/css.ini +19 -0
  134. preditor/scintilla/lang/config/eyeonscript.ini +17 -0
  135. preditor/scintilla/lang/config/html.ini +21 -0
  136. preditor/scintilla/lang/config/javascript.ini +24 -0
  137. preditor/scintilla/lang/config/lua.ini +16 -0
  138. preditor/scintilla/lang/config/maxscript.ini +20 -0
  139. preditor/scintilla/lang/config/mel.ini +18 -0
  140. preditor/scintilla/lang/config/mu.ini +22 -0
  141. preditor/scintilla/lang/config/nsi.ini +19 -0
  142. preditor/scintilla/lang/config/perl.ini +19 -0
  143. preditor/scintilla/lang/config/puppet.ini +19 -0
  144. preditor/scintilla/lang/config/python.ini +28 -0
  145. preditor/scintilla/lang/config/ruby.ini +19 -0
  146. preditor/scintilla/lang/config/sql.ini +7 -0
  147. preditor/scintilla/lang/config/xml.ini +21 -0
  148. preditor/scintilla/lang/config/yaml.ini +18 -0
  149. preditor/scintilla/lang/language.py +240 -0
  150. preditor/scintilla/lexers/__init__.py +0 -0
  151. preditor/scintilla/lexers/cpplexer.py +22 -0
  152. preditor/scintilla/lexers/javascriptlexer.py +27 -0
  153. preditor/scintilla/lexers/maxscriptlexer.py +235 -0
  154. preditor/scintilla/lexers/mellexer.py +369 -0
  155. preditor/scintilla/lexers/mulexer.py +33 -0
  156. preditor/scintilla/lexers/pythonlexer.py +42 -0
  157. preditor/scintilla/ui/finddialog.ui +160 -0
  158. preditor/settings.py +71 -0
  159. preditor/stream/__init__.py +72 -0
  160. preditor/stream/console_handler.py +169 -0
  161. preditor/stream/director.py +144 -0
  162. preditor/stream/manager.py +97 -0
  163. preditor/streamhandler_helper.py +46 -0
  164. preditor/utils/__init__.py +191 -0
  165. preditor/utils/call_stack.py +86 -0
  166. preditor/utils/cute.py +106 -0
  167. preditor/utils/stylesheets.py +54 -0
  168. preditor/utils/text_search.py +338 -0
  169. preditor/version.py +34 -0
  170. preditor/weakref.py +363 -0
  171. preditor-2.1.0.dist-info/METADATA +308 -0
  172. preditor-2.1.0.dist-info/RECORD +179 -0
  173. preditor-2.1.0.dist-info/WHEEL +5 -0
  174. preditor-2.1.0.dist-info/entry_points.txt +19 -0
  175. preditor-2.1.0.dist-info/licenses/LICENSE +165 -0
  176. preditor-2.1.0.dist-info/top_level.txt +3 -0
  177. tests/encodings/test_ecoding.py +33 -0
  178. tests/find_files/test_find_files.py +74 -0
  179. tests/ide/test_delayable_engine.py +171 -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
+ # =============================================================================