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/config.py ADDED
@@ -0,0 +1,318 @@
1
+ from __future__ import absolute_import
2
+
3
+ import logging
4
+ from functools import wraps
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ def set_if_unlocked(track_state=False):
10
+ def _set_if_unlocked(func):
11
+ """Decorate property setter functions to prevent editing after locking."""
12
+
13
+ @wraps(func)
14
+ def wrapper(self, value):
15
+ if not self.is_locked(func.__name__, track_state=track_state):
16
+ # logger.debug(f'"{func.__name__}" is unlocked and can be set: {value}')
17
+ return func(self, value)
18
+ # logger.info(
19
+ # f'"{func.__name__}" is locked and can no-longer be set: {value}'
20
+ # )
21
+
22
+ return wrapper
23
+
24
+ return _set_if_unlocked
25
+
26
+
27
+ class PreditorConfig:
28
+ """Global configuration of PrEditor's instance.
29
+
30
+ When creating the main PrEditor instance it will use `preditor.config` to
31
+ control how its initialized. That stores an instance of this class.
32
+
33
+ Once `preditor.instance(create=True)` the properties on this class will then
34
+ silently ignore any attempts to set most of its properties. Where noted in
35
+ the property doc-strings, some properties are used to install a setting that
36
+ can't be automatically undone, so they will stop responding after the first
37
+ setting.
38
+
39
+ While this class does not force a singleton pattern, it is expected in normal
40
+ operation that there will only ever be a single instance made and it is stored
41
+ on `preditor.config` when first imported. Things like excepthook and streams
42
+ modify python's state which could lead to duplicate outputs, multiple error
43
+ prompts or other undesirable behavior.
44
+ """
45
+
46
+ def __init__(self):
47
+ self._locks = {}
48
+ self._name = None
49
+ self._logging = False
50
+ self._headless_callback = None
51
+ self._on_create_callback = None
52
+ self._parent_callback = None
53
+ self._error_dialog_class = True
54
+ self._excepthooks = []
55
+
56
+ def dump(self, indent=0):
57
+ """A convenient way to inspect the current configuration."""
58
+ ret = [
59
+ f"{' ' * indent}name: {self.name}",
60
+ f"{' ' * indent}streams: {self.streams}",
61
+ f"{' ' * indent}excepthook: {self.excepthook!r}",
62
+ f"{' ' * indent}excepthooks: {self.excepthooks!r}",
63
+ f"{' ' * indent}error_dialog_class: {self.error_dialog_class!r}",
64
+ f"{' ' * indent}logging: {self.logging}",
65
+ f"{' ' * indent}headless_callback: {self.headless_callback!r}",
66
+ f"{' ' * indent}parent_callback: {self.parent_callback!r}",
67
+ f"{' ' * indent}on_create_callback: {self.on_create_callback!r}",
68
+ ]
69
+ return '\n'.join(ret)
70
+
71
+ def is_locked(self, value, track_state=False):
72
+ """Returns if value is locked and can not be updated.
73
+
74
+ Once `preditor.instance` returns a value this will always return False.
75
+ The config settings should not be modified once the instance is created.
76
+
77
+ Args:
78
+ value (str): The name of the value to check. Should match a property
79
+ name on this class.
80
+ track_state (bool, optional): If True then also check if value has
81
+ been flagged as locked. This indicates that it was already set
82
+ by a previous call and can no longer be modified even if instance
83
+ is False.
84
+ """
85
+ if self.instance(create=False):
86
+ # if the instance is created all items are locked
87
+ return True
88
+ if track_state:
89
+ return self._locks.get(value, False)
90
+ return False
91
+
92
+ @property
93
+ def error_dialog_class(self):
94
+ """Dialog class shown if PrEditor isn't visible and a error happens.
95
+
96
+ This dialog is used to prompt the user to show PrEditor and can be
97
+ sub-classed to add extra functionality. This is called by
98
+ `PreditorExceptHook.ask_to_show_logger` method when added to
99
+ `preditor.config.excepthooks`.
100
+
101
+ If set to `True` then `blurdev.gui.errordialog.ErrorDialog` is used. When
102
+ replacing this, it should be a sub-class of that class or re-implement
103
+ its api.
104
+
105
+ You can use an EntryPoint string like `preditor.gui.errordialog:ErrorDialog`
106
+ instead of passing the actual class object. This lets you delay the import
107
+ until actually needed.
108
+ """
109
+ return self._error_dialog_class
110
+
111
+ @error_dialog_class.setter
112
+ def error_dialog_class(self, cls):
113
+ self._error_dialog_class = cls
114
+
115
+ @property
116
+ def excepthook(self):
117
+ """Installs a `sys.excepthook` handler when first set to True.
118
+
119
+ Replaces `sys.excepthook` with a interactive exception handler that
120
+ prompts the user to show PrEditor when an python exception is raised.
121
+ It is recommended that you only add the excepthook once the Qt UI is
122
+ initialized. See: :py:class:`preditor.excepthooks.PreditorExceptHook`.
123
+ """
124
+ return self._locks.get("excepthook", False)
125
+
126
+ @excepthook.setter
127
+ @set_if_unlocked(track_state=True)
128
+ def excepthook(self, value):
129
+ if not value:
130
+ return
131
+
132
+ # Install the excepthook:
133
+ import preditor.excepthooks
134
+
135
+ # Note: install checks if the current excepthook is a instance of this
136
+ # class and prevents installing a second time.
137
+ preditor.excepthooks.PreditorExceptHook.install()
138
+
139
+ # Disable future setting via `set_if_unlocked`, the `PreditorExceptHook`
140
+ # is a chaining except hook that calls the previous excepthook when called.
141
+ # We don't want to install multiple automatically, the user can define that
142
+ # logic if required.
143
+ self._locks["excepthook"] = True
144
+
145
+ @property
146
+ def excepthooks(self):
147
+ """A list of callables that are called when an exception is handled.
148
+
149
+ If `excepthook` is enabled installing `PreditorExceptHook` then it will
150
+ call each item in this list. The signature of the function should be
151
+ `callable(*args)`. If not configured it will automatically install default
152
+ callables. You can add `None` to disable this.
153
+ """
154
+ return self._excepthooks
155
+
156
+ @property
157
+ def headless_callback(self):
158
+ """A pointer to a method that is called by `is_headless`.
159
+
160
+ This callback returns a bool indicating if PrEditor should attempt to
161
+ create GUI elements. Application integrations can set this callback to
162
+ give them control over what PrEditor gets parent to.
163
+ """
164
+ return self._headless_callback
165
+
166
+ @headless_callback.setter
167
+ @set_if_unlocked()
168
+ def headless_callback(self, cb):
169
+ self._headless_callback = cb
170
+
171
+ @classmethod
172
+ def instance(cls, parent=None, run_workbox=False, create=True):
173
+ """Returns the existing instance of the PrEditor gui creating it on first call.
174
+
175
+ Args:
176
+ parent (QWidget, optional): If the instance hasn't been created yet, create
177
+ it and parent it to this object.
178
+ run_workbox (bool, optional): If the instance hasn't been created yet, this
179
+ will execute the active workbox's code once fully initialized.
180
+ create (bool, optional): Returns None if the instance has not been created.
181
+
182
+ Returns:
183
+ Returns a fully initialized instance of the PrEditor gui. If called more
184
+ than once, the same instance will be returned. If create is False, it may
185
+ return None.
186
+ """
187
+ from .gui.loggerwindow import LoggerWindow
188
+
189
+ return LoggerWindow.instance(
190
+ parent=parent, run_workbox=run_workbox, create=create
191
+ )
192
+
193
+ def is_headless(self):
194
+ """Returns True if PrEditor should not create GUI items.
195
+
196
+ Returns None if the `headless_callback` has not been configured. Otherwise
197
+ returns the result of calling `headless_callback()` which should return a bool.
198
+ """
199
+ if not self.headless_callback:
200
+ return None
201
+ return self.headless_callback()
202
+
203
+ @property
204
+ def logging(self):
205
+ """Restore the python logging configuration settings that were recorded
206
+ the last time PrEditor prefs were saved.
207
+
208
+ If called multiple times with different name's before the instance is
209
+ created, this will reset the logging configuration from the previous
210
+ name if logging prefs exist.
211
+ """
212
+ return self._logging
213
+
214
+ @logging.setter
215
+ def logging(self, state):
216
+ self._logging = state
217
+ self.update_logging()
218
+
219
+ @property
220
+ def name(self):
221
+ """The name to use for the global instance of PrEditor.
222
+
223
+ Once this has been set, you can call `preditor.launch` without passing
224
+ name to access the main instance. The name controls what preferences
225
+ are loaded and used by PrEditor including the workbox tabs."""
226
+ return self._name
227
+
228
+ @name.setter
229
+ @set_if_unlocked()
230
+ def name(self, name):
231
+ changed = self.name != name
232
+ self._name = name
233
+
234
+ if changed:
235
+ # If the core name was changed attempt to update the logging config.
236
+ self.update_logging()
237
+
238
+ @property
239
+ def on_create_callback(self):
240
+ """A pointer to a method that is called on LoggerWindow instance create.
241
+
242
+ This callback accepts the instance and can be used to customize the
243
+ LoggerWindow instance when it is first created.
244
+ """
245
+ return self._on_create_callback
246
+
247
+ @on_create_callback.setter
248
+ @set_if_unlocked()
249
+ def on_create_callback(self, cb):
250
+ self._on_create_callback = cb
251
+
252
+ @property
253
+ def parent_callback(self):
254
+ """A pointer to a method that is called by `self.root_window`.
255
+
256
+ This callback returns a QWidget to use as the parent of the LoggerWindow
257
+ when the instance is first created. This can be used by DCC's to set the
258
+ parent to their main window."""
259
+ return self._parent_callback
260
+
261
+ @parent_callback.setter
262
+ @set_if_unlocked()
263
+ def parent_callback(self, cb):
264
+ self._parent_callback = cb
265
+
266
+ def root_window(self):
267
+ """The QWidget to use as the parent of PrEditor widgets."""
268
+ # If a parent widget callback was configured, use it
269
+ if self.parent_callback is not None:
270
+ return self.parent_callback()
271
+
272
+ # Otherwise, attempt to find the top level widget from Qt
273
+ from .gui.app import App
274
+
275
+ return App.root_window()
276
+
277
+ @property
278
+ def streams(self):
279
+ """Installs the stream managers when first set to True.
280
+
281
+ Install the stream manager to capture any stdout/stderr text written.
282
+ Later when calling launch, the LoggerWindow will show all of the captured
283
+ text. This lets you only create the LoggerWindow IF you need to show it,
284
+ but when you do it will have all of the std stream text written after
285
+ this is enabled."""
286
+ return self._locks.get("streams", False)
287
+
288
+ @streams.setter
289
+ @set_if_unlocked(track_state=True)
290
+ def streams(self, value):
291
+ if not value:
292
+ return
293
+
294
+ # Install the stream manager to capture output
295
+ from .stream import install_to_std
296
+
297
+ install_to_std()
298
+
299
+ # Disable re-installing streams.
300
+ self._locks["streams"] = True
301
+
302
+ def update_logging(self):
303
+ """Install/replace the logging config.
304
+
305
+ This does nothing unless `logging` is set to True and `name` is set.
306
+
307
+ Note: When called repeatedly, this won't remove old logger's added by
308
+ previous calls so you may see some loggers added that were never
309
+ actually added by code other than the LoggingConfig.
310
+ """
311
+
312
+ if not (self.logging and self.name):
313
+ return
314
+
315
+ from .logging_config import LoggingConfig
316
+
317
+ cfg = LoggingConfig(core_name=self.name)
318
+ cfg.load()
preditor/constants.py ADDED
@@ -0,0 +1,13 @@
1
+ import enum
2
+
3
+
4
+ class StreamType(enum.Flag):
5
+ """Different types of streams used by PrEditor."""
6
+
7
+ STDERR = enum.auto()
8
+ STDIN = enum.auto()
9
+ STDOUT = enum.auto()
10
+ CONSOLE = enum.auto()
11
+ """Write directly to the console ignoring STDERR/STDOUT filters."""
12
+ RESULT = enum.auto()
13
+ """Write directly to ConsolePrEdit's result output without using stdout/err."""
preditor/contexts.py ADDED
@@ -0,0 +1,210 @@
1
+ from __future__ import absolute_import
2
+
3
+ import logging
4
+ from contextlib import contextmanager
5
+ from typing import List, Tuple
6
+
7
+ _LOGGER = logging.getLogger(__name__)
8
+
9
+
10
+ class ErrorReport(object):
11
+ """Allows you to provide additional debug info if a error happens in this context.
12
+
13
+ PrEditor can send a error email when any python error is raised.
14
+ Sometimes just a traceback does not provide enough information to debug the
15
+ traceback. This class allows you to provide additional information to the error
16
+ report only if it is generated. For example if your treegrunt environment does not
17
+ have a email setup, or the current debug level is not set to Disabled.
18
+
19
+ ErrorReport can be used as a with context, or as a function decorator.
20
+
21
+ Examples:
22
+ This example shows a class using both the with context and a decorated method.
23
+
24
+ from preditor.contexts import ErrorReport
25
+ class Test(object):
26
+ def __init__(self):
27
+ self.value = None
28
+ def errorInfo(self):
29
+ # The text returned by this function will be included in the error email
30
+ return 'Info about the Test class: {}'.format(self.value)
31
+ def doStuff(self):
32
+ with ErrorReport(self.errorInfo, 'Test.doStuff'):
33
+ self.value = 'doStuff'
34
+ raise RuntimeError("BILL")
35
+ @ErrorReport(errorInfo, 'Test.doMoreStuff')
36
+ def doMoreStuff(self):
37
+ self.value = 'doMoreStuff'
38
+ raise RuntimeError("BOB")
39
+
40
+ Using this class does not initialize the Python Logger, so you don't need to worry
41
+ if your class is running headless and not use this class. However unless you set up
42
+ your own error reporting system the callbacks will not be called and nothing will be
43
+ reported.
44
+
45
+ If you want to set up your own error reporting system you need to set
46
+ `ErrorReport.enabled = True`. Then you will need to call ErrorReport.clearReports()
47
+ any time excepthook is called. This prevents a buildup of all error reports any time
48
+ a exception occurs. It should always be in place when you set enabled == True to
49
+ prevent wasting memory. Calling ErrorReport.generateReport() will return the info
50
+ you should include in your report. Calling generateReport is optional, but must be
51
+ called before calling clearReports.
52
+
53
+ Args:
54
+ callback (function): If a exception happens this function is called and its
55
+ returned value is added to the error email if sent. No arguments are passed
56
+ to this function and it is expected to only return a string.
57
+ title (str, optional): This short string is added to the title of the
58
+ ErrorReport.
59
+ Attributes:
60
+ enabled (bool): If False(the default), then all callbacks are cleared even if
61
+ there is a exception. This is used to prevent these functions from leaking
62
+ memory if there isn't a excepthook calling clearReports.
63
+ """
64
+
65
+ __reports__: List[Tuple[str, str]] = []
66
+ enabled = False
67
+
68
+ def __init__(self, callback, title=''):
69
+ self._callback = callback
70
+ self._title = title
71
+
72
+ def __call__(self, funct):
73
+ def wrapper(wrappedSelf, *args, **kwargs):
74
+ unbound = self._callback
75
+ self._callback = self._callback.__get__(wrappedSelf)
76
+ try:
77
+ with self:
78
+ return funct(wrappedSelf, *args, **kwargs)
79
+ finally:
80
+ self._callback = unbound
81
+
82
+ return wrapper
83
+
84
+ def __enter__(self):
85
+ type(self).__reports__.append((self._title, self._callback))
86
+
87
+ def __exit__(self, exc_type, exc_val, exc_tb):
88
+ # If exc_type is None, then no exception was raised, so we should remove the
89
+ # callback. If cls.enabled is False, then nothing has set itself up to call
90
+ # clearReports. We need to remove the callback so it doesn't stay in memory.
91
+ if exc_type is None or not type(self).enabled:
92
+ type(self).__reports__.remove((self._title, self._callback))
93
+
94
+ @classmethod
95
+ def clearReports(cls):
96
+ """Removes all of the currently stored callbacks.
97
+
98
+ This should be called after all error reporting is finished, or if a error
99
+ happened and there is nothing to report it. If you set cls.enabled to True,
100
+ something in excepthook should call this to prevent keeping refrences to
101
+ functions from staying in memory.
102
+ """
103
+ cls.__reports__ = []
104
+
105
+ @classmethod
106
+ def generateReport(cls, fmt='{result}'):
107
+ """Executes and returns all of the currently stored callbacks.
108
+ Args:
109
+
110
+ ftm (str, Optional): The results of the callbacks will be inserted into this
111
+ string using str.format into {results}.
112
+ Returns:
113
+ list: A list of tuples for all active ErrorReport classes. The tuples
114
+ contain two strings; the title string, and result of the passed in
115
+ callback function.
116
+ """
117
+ ret = []
118
+ for title, callback in cls.__reports__:
119
+ result = callback()
120
+ ret.append((title, fmt.format(result=result)))
121
+ return ret
122
+
123
+
124
+ @contextmanager
125
+ def OverrideConsoleStreams(
126
+ consoles,
127
+ tracebacks=None,
128
+ stdout=None,
129
+ stderr=None,
130
+ result=None,
131
+ logging_handlers=None,
132
+ ):
133
+ """Override a Console's stream and logging_handlers settings.
134
+
135
+ This is useful if you don't want to show tracebacks or output for un-related code.
136
+
137
+ Example:
138
+
139
+ console = OutputConsole()
140
+ with OverrideConsoleStreams(console, tracebacks=True)
141
+ # A traceback raised here would show in console
142
+ do_work_you_only_want_to_show_tracebacks_in_console()
143
+ raise RuntimeError("This traceback will not be shown in console")
144
+ """
145
+ # If a single console was passed, convert it into a list
146
+ try:
147
+ iter(consoles)
148
+ except TypeError:
149
+ consoles = [consoles]
150
+
151
+ # Store the current state and apply changes to the consoles
152
+ current = []
153
+ for console in consoles:
154
+ current.append(
155
+ (
156
+ console,
157
+ None if tracebacks is None else console.stream_echo_tracebacks,
158
+ None if stdout is None else console.stream_echo_stdout,
159
+ None if stderr is None else console.stream_echo_stderr,
160
+ None if result is None else console.stream_echo_result,
161
+ None if logging_handlers is None else console.logging_handlers,
162
+ )
163
+ )
164
+
165
+ if tracebacks is not None:
166
+ # Enable/disable traceback display override
167
+ console.stream_echo_tracebacks = tracebacks
168
+ if tracebacks:
169
+ # traceback display is enabled, make it automatically remove
170
+ # itself if an exception is raised by the sys.excepthook handler.
171
+ console._write_error_self_destruct = True
172
+ if stdout is not None:
173
+ console.stream_echo_stdout = stdout
174
+ if stderr is not None:
175
+ console.stream_echo_stderr = stderr
176
+ if result is not None:
177
+ console.stream_echo_result = result
178
+ if logging_handlers is not None:
179
+ console.logging_handlers = logging_handlers
180
+
181
+ # NOTE: This code is a horrible abomination of necessity.
182
+ try:
183
+ yield
184
+ except Exception:
185
+ # This is syntactically needed, we don't need to capture the exception,
186
+ # but the else requires it. We need raise any un-handled exceptions
187
+ # encountered to allow sys.excepthook to process normally.
188
+ # This prevents the else code from removing the traceback print overriding
189
+ # because the else/finally are processed before sys.excepthook is.
190
+ raise
191
+ else:
192
+ # Only called when exception was not raised. Restore the original traceback
193
+ # and disable the self destructing flag its not needed anymore.
194
+ for item in current:
195
+ if tracebacks is not None:
196
+ item[0].stream_echo_tracebacks = item[1]
197
+ if tracebacks:
198
+ item[0]._write_error_self_destruct = False
199
+ finally:
200
+ # Always remove non-traceback options. This is handled before excepthook
201
+ # so we can just use the simplicity of a normal try/finally statement.
202
+ for item in current:
203
+ if stdout is not None:
204
+ item[0].stream_echo_stdout = item[2]
205
+ if stderr is not None:
206
+ item[0].stream_echo_stderr = item[3]
207
+ if result is not None:
208
+ item[0].stream_echo_result = item[4]
209
+ if logging_handlers is not None:
210
+ item[0].logging_handlers = item[5]
File without changes
preditor/cores/core.py ADDED
@@ -0,0 +1,20 @@
1
+ from __future__ import absolute_import, print_function
2
+
3
+ from Qt.QtCore import QObject
4
+
5
+
6
+ class Core(QObject):
7
+ """
8
+ The Core class provides all the main shared functionality and signals that need to
9
+ be distributed between different pacakges.
10
+ """
11
+
12
+ def __init__(self, objectName=None):
13
+ super(Core, self).__init__()
14
+ if objectName is None:
15
+ objectName = 'PrEditor'
16
+ self.setObjectName(objectName)
17
+
18
+ # Paths in this variable will be removed in
19
+ # preditor.osystem.subprocessEnvironment
20
+ self._removeFromPATHEnv = set()
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "preditor",
3
+ "version": "0.0.dev0",
4
+ "environment": {
5
+ "append": {
6
+ "MAYA_MODULE_PATH": "{relative_root}/maya",
7
+ "ADSK_APPLICATION_PLUGINS": "{relative_root}/studiomax"
8
+ }
9
+ }
10
+ }
@@ -0,0 +1 @@
1
+ + PrEditor DEVELOPMENT .
@@ -0,0 +1,22 @@
1
+ # Maya Integration
2
+
3
+ This is an example of using an Maya module to add PrEditor into Maya. This adds
4
+ a PrEditor menu with a PrEditor action in Maya's menu bar letting you open PrEditor. It
5
+ adds the excepthook so if a python exception is raised it will prompt the user
6
+ to show PrEditor. PrEditor will show all python stdout/stderr output generated
7
+ after the plugin is loaded.
8
+
9
+ # Setup
10
+
11
+ Make sure to follow these [setup instructions](/preditor/README.md#Setup) first to create the virtualenv.
12
+
13
+ Alternatively you can use [myapy's](https://help.autodesk.com/view/MAYAUL/2026/ENU/?guid=GUID-72A245EC-CDB4-46AB-BEE0-4BBBF9791627) pip to install the requirements, but a
14
+ separate virtualenv is recommended. This method should not require setting the
15
+ `PREDITOR_SITE` environment variable even if you use an editable install.
16
+
17
+ # Use
18
+
19
+ The [preditor/dccs/maya](/preditor/dccs/maya) directory is setup as a Maya Module. To load it in
20
+ maya add the full path to that directory to the `MAYA_MODULE_PATH` environment
21
+ variable. You can use `;` on windows and `:` on linux to join multiple paths together.
22
+ You will need to enable auto load for the `PrEditor_maya.py` plugin.