PrEditor 1.4.0__py3-none-any.whl → 1.5.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/debug.py +3 -3
- preditor/gui/console.py +151 -40
- preditor/gui/loggerwindow.py +1 -1
- preditor/gui/workbox_mixin.py +2 -2
- preditor/gui/workboxwidget.py +4 -1
- preditor/scintilla/documenteditor.py +1 -9
- preditor/stream/director.py +87 -16
- preditor/version.py +3 -3
- {preditor-1.4.0.dist-info → preditor-1.5.0.dist-info}/METADATA +1 -1
- {preditor-1.4.0.dist-info → preditor-1.5.0.dist-info}/RECORD +14 -14
- {preditor-1.4.0.dist-info → preditor-1.5.0.dist-info}/WHEEL +0 -0
- {preditor-1.4.0.dist-info → preditor-1.5.0.dist-info}/entry_points.txt +0 -0
- {preditor-1.4.0.dist-info → preditor-1.5.0.dist-info}/licenses/LICENSE +0 -0
- {preditor-1.4.0.dist-info → preditor-1.5.0.dist-info}/top_level.txt +0 -0
preditor/debug.py
CHANGED
|
@@ -32,9 +32,9 @@ class FileLogger:
|
|
|
32
32
|
return msg.format(today=datetime.datetime.today(), version=sys.version)
|
|
33
33
|
|
|
34
34
|
def write(self, msg):
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
with open(self._logfile, 'a', encoding="utf-8") as f:
|
|
36
|
+
f.write(msg)
|
|
37
|
+
|
|
38
38
|
if self._print:
|
|
39
39
|
self._stdhandle.write(msg)
|
|
40
40
|
|
preditor/gui/console.py
CHANGED
|
@@ -53,6 +53,16 @@ class ConsolePrEdit(QTextEdit):
|
|
|
53
53
|
# If populated, also write to this interface
|
|
54
54
|
self.outputPipe = None
|
|
55
55
|
|
|
56
|
+
# For workboxes, use this regex pattern, so we can extract workboxName
|
|
57
|
+
# and lineNum
|
|
58
|
+
pattern = r'File "<Workbox(?:Selection)?>:(?P<workboxName>.*)", '
|
|
59
|
+
pattern += r'line (?P<lineNum>\d{1,6}), in'
|
|
60
|
+
self.workbox_pattern = re.compile(pattern)
|
|
61
|
+
|
|
62
|
+
# Define a pattern to capture info from tracebacks
|
|
63
|
+
pattern = r'File "(?P<filename>.*)", line (?P<lineNum>\d{1,10}), in'
|
|
64
|
+
self.traceback_pattern = re.compile(pattern)
|
|
65
|
+
|
|
56
66
|
self._consolePrompt = '>>> '
|
|
57
67
|
# Note: Changing _outputPrompt may require updating resource\lang\python.xml
|
|
58
68
|
# If still using a #
|
|
@@ -90,7 +100,7 @@ class ConsolePrEdit(QTextEdit):
|
|
|
90
100
|
# to the console and free up the memory consumed by previous writes as we
|
|
91
101
|
# assume this is likely to be the only callback added to the manager.
|
|
92
102
|
self.stream_manager.add_callback(
|
|
93
|
-
self.
|
|
103
|
+
self.pre_write, replay=True, disable_writes=True, clear=True
|
|
94
104
|
)
|
|
95
105
|
# Store the current outputs
|
|
96
106
|
self.stdout = sys.stdout
|
|
@@ -122,6 +132,11 @@ class ConsolePrEdit(QTextEdit):
|
|
|
122
132
|
if not self.cursorWidth():
|
|
123
133
|
self.setCursorWidth(1)
|
|
124
134
|
|
|
135
|
+
# The act of changing from no scroll bar to a scroll bar can add up to 1
|
|
136
|
+
# second of time to the process of outputting text, so, just always have
|
|
137
|
+
# it on.
|
|
138
|
+
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
|
|
139
|
+
|
|
125
140
|
def doubleSingleShotSetScrollValue(self, origPercent):
|
|
126
141
|
"""This double QTimer.singleShot monkey business seems to be the only way
|
|
127
142
|
to get scroll.maximum() to update properly so that we calc newValue
|
|
@@ -239,7 +254,7 @@ class ConsolePrEdit(QTextEdit):
|
|
|
239
254
|
# info is a comma separated string, in the form: "filename, workboxIdx, lineNum"
|
|
240
255
|
info = self.anchor.split(', ')
|
|
241
256
|
modulePath = info[0]
|
|
242
|
-
|
|
257
|
+
workboxName = info[1]
|
|
243
258
|
lineNum = info[2]
|
|
244
259
|
|
|
245
260
|
# fetch info from LoggerWindow
|
|
@@ -250,23 +265,27 @@ class ConsolePrEdit(QTextEdit):
|
|
|
250
265
|
cmdTempl = window.textEditorCmdTempl
|
|
251
266
|
|
|
252
267
|
# Bail if not setup properly
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
268
|
+
if workboxName is None:
|
|
269
|
+
msg = (
|
|
270
|
+
"Cannot use traceback hyperlink (Correct the path with Options "
|
|
271
|
+
"> Set Preferred Text Editor Path).\n"
|
|
272
|
+
)
|
|
273
|
+
if not exePath:
|
|
274
|
+
msg += "No text editor path defined."
|
|
275
|
+
print(msg)
|
|
276
|
+
return
|
|
277
|
+
if not os.path.exists(exePath):
|
|
278
|
+
msg += "Text editor executable does not exist: {}".format(exePath)
|
|
279
|
+
print(msg)
|
|
280
|
+
return
|
|
281
|
+
if not cmdTempl:
|
|
282
|
+
msg += "No text editor Command Prompt command template defined."
|
|
283
|
+
print(msg)
|
|
284
|
+
return
|
|
285
|
+
if modulePath and not os.path.exists(modulePath):
|
|
286
|
+
msg += "Specified module path does not exist: {}".format(modulePath)
|
|
287
|
+
print(msg)
|
|
288
|
+
return
|
|
270
289
|
|
|
271
290
|
if modulePath:
|
|
272
291
|
# Check if cmdTempl filepaths aren't wrapped in double=quotes to handle
|
|
@@ -296,12 +315,9 @@ class ConsolePrEdit(QTextEdit):
|
|
|
296
315
|
msg = "The provided text editor command template is not valid:\n {}"
|
|
297
316
|
msg = msg.format(cmdTempl)
|
|
298
317
|
print(msg)
|
|
299
|
-
elif
|
|
300
|
-
|
|
318
|
+
elif workboxName is not None:
|
|
319
|
+
workbox = window.workbox_for_name(workboxName, visible=True)
|
|
301
320
|
lineNum = int(lineNum)
|
|
302
|
-
workbox = window.uiWorkboxTAB.set_current_groups_from_index(
|
|
303
|
-
int(group), int(editor)
|
|
304
|
-
)
|
|
305
321
|
workbox.__goto_line__(lineNum)
|
|
306
322
|
workbox.setFocus()
|
|
307
323
|
|
|
@@ -373,7 +389,34 @@ class ConsolePrEdit(QTextEdit):
|
|
|
373
389
|
"""returns the completer instance that is associated with this editor"""
|
|
374
390
|
return self._completer
|
|
375
391
|
|
|
376
|
-
def
|
|
392
|
+
def getWorkboxLine(self, name, lineNum):
|
|
393
|
+
"""Python 3 does not include in tracebacks the code line if it comes from
|
|
394
|
+
stdin, which is the case for PrEditor workboxes, so we fake it. This method
|
|
395
|
+
will return the line of code at lineNum, from the workbox with the provided
|
|
396
|
+
name.
|
|
397
|
+
|
|
398
|
+
Args:
|
|
399
|
+
name (str): The name of the workbox from which to get a line of code
|
|
400
|
+
lineNum (int): The number of the line to return
|
|
401
|
+
|
|
402
|
+
Returns:
|
|
403
|
+
txt (str): The line of text found
|
|
404
|
+
"""
|
|
405
|
+
workbox = self.window().workbox_for_name(name)
|
|
406
|
+
if not workbox:
|
|
407
|
+
return None
|
|
408
|
+
if lineNum > workbox.lines():
|
|
409
|
+
return None
|
|
410
|
+
txt = workbox.text(lineNum).strip() + "\n"
|
|
411
|
+
return txt
|
|
412
|
+
|
|
413
|
+
def executeString(
|
|
414
|
+
self, commandText, consoleLine=None, filename='<ConsolePrEdit>', extraPrint=True
|
|
415
|
+
):
|
|
416
|
+
# These vars helps with faking code lines in tracebacks for stdin input, which
|
|
417
|
+
# workboxes are, and py3 doesn't include in the traceback
|
|
418
|
+
self.consoleLine = consoleLine or ""
|
|
419
|
+
|
|
377
420
|
if self.clearExecutionTime is not None:
|
|
378
421
|
self.clearExecutionTime()
|
|
379
422
|
cursor = self.textCursor()
|
|
@@ -428,6 +471,10 @@ class ConsolePrEdit(QTextEdit):
|
|
|
428
471
|
|
|
429
472
|
def executeCommand(self):
|
|
430
473
|
"""executes the current line of code"""
|
|
474
|
+
|
|
475
|
+
# Not using workbox, so clear this
|
|
476
|
+
self.consoleLine = ""
|
|
477
|
+
|
|
431
478
|
# grab the command from the line
|
|
432
479
|
block = self.textCursor().block().text()
|
|
433
480
|
p = '{prompt}(.*)'.format(prompt=re.escape(self.prompt()))
|
|
@@ -450,7 +497,9 @@ class ConsolePrEdit(QTextEdit):
|
|
|
450
497
|
self._prevCommands = self._prevCommands[-1 * self._prevCommandsMax :]
|
|
451
498
|
|
|
452
499
|
# evaluate the command
|
|
453
|
-
cmdresult, wasEval = self.executeString(
|
|
500
|
+
cmdresult, wasEval = self.executeString(
|
|
501
|
+
commandText, consoleLine=commandText
|
|
502
|
+
)
|
|
454
503
|
|
|
455
504
|
# print the resulting commands
|
|
456
505
|
if cmdresult is not None:
|
|
@@ -787,28 +836,65 @@ class ConsolePrEdit(QTextEdit):
|
|
|
787
836
|
"""Determine if txt is a File-info line from a traceback, and if so, return info
|
|
788
837
|
dict.
|
|
789
838
|
"""
|
|
790
|
-
|
|
839
|
+
|
|
791
840
|
ret = None
|
|
841
|
+
if not txt.lstrip().startswith("File "):
|
|
842
|
+
return ret
|
|
843
|
+
|
|
844
|
+
match = self.traceback_pattern.search(txt)
|
|
845
|
+
if match:
|
|
846
|
+
filename = match.groupdict().get('filename')
|
|
847
|
+
lineNum = match.groupdict().get('lineNum')
|
|
848
|
+
fileStart = txt.find(filename)
|
|
849
|
+
fileEnd = fileStart + len(filename)
|
|
792
850
|
|
|
793
|
-
filenameEnd = txt.find(lineMarker)
|
|
794
|
-
if txt[:8] == ' File "' and filenameEnd >= 0:
|
|
795
|
-
filename = txt[8:filenameEnd]
|
|
796
|
-
lineNumStart = filenameEnd + len(lineMarker)
|
|
797
|
-
lineNumEnd = txt.find(',', lineNumStart)
|
|
798
|
-
if lineNumEnd == -1:
|
|
799
|
-
lineNumEnd = len(txt)
|
|
800
|
-
lineNum = txt[lineNumStart:lineNumEnd]
|
|
801
851
|
ret = {
|
|
802
852
|
'filename': filename,
|
|
803
|
-
'fileStart':
|
|
804
|
-
'fileEnd':
|
|
853
|
+
'fileStart': fileStart,
|
|
854
|
+
'fileEnd': fileEnd,
|
|
805
855
|
'lineNum': lineNum,
|
|
806
856
|
}
|
|
807
|
-
|
|
808
857
|
return ret
|
|
809
858
|
|
|
859
|
+
@staticmethod
|
|
860
|
+
def getIndentForCodeTracebackLine(msg):
|
|
861
|
+
"""Determine the indentation to recreate traceback lines
|
|
862
|
+
|
|
863
|
+
Args:
|
|
864
|
+
msg (str): The traceback line
|
|
865
|
+
|
|
866
|
+
Returns:
|
|
867
|
+
indent (str): A string of zero or more spaces used for indentation
|
|
868
|
+
"""
|
|
869
|
+
indent = ""
|
|
870
|
+
match = re.match(r"^ *", msg)
|
|
871
|
+
if match:
|
|
872
|
+
indent = match.group() * 2
|
|
873
|
+
return indent
|
|
874
|
+
|
|
875
|
+
def pre_write(self, msg, error=False):
|
|
876
|
+
"""In order to make a stack-trace provide clickable hyperlinks, it must be sent
|
|
877
|
+
to self.write line-by-line, like a actual exception traceback is. So, we check
|
|
878
|
+
if msg has the stack marker str, if so, send it line by line, otherwise, just
|
|
879
|
+
pass msg on to self.write.
|
|
880
|
+
"""
|
|
881
|
+
stack_marker = "Stack (most recent call last)"
|
|
882
|
+
index = msg.find(stack_marker)
|
|
883
|
+
has_stack_marker = index > -1
|
|
884
|
+
|
|
885
|
+
if has_stack_marker:
|
|
886
|
+
lines = msg.split("\n")
|
|
887
|
+
for line in lines:
|
|
888
|
+
line = "{}\n".format(line)
|
|
889
|
+
self.write(line, error=error)
|
|
890
|
+
else:
|
|
891
|
+
self.write(msg, error=error)
|
|
892
|
+
|
|
810
893
|
def write(self, msg, error=False):
|
|
811
894
|
"""write the message to the logger"""
|
|
895
|
+
if not msg:
|
|
896
|
+
return
|
|
897
|
+
|
|
812
898
|
# Convert the stream_manager's stream to the boolean value this function expects
|
|
813
899
|
error = error == stream.STDERR
|
|
814
900
|
# Check that we haven't been garbage collected before trying to write.
|
|
@@ -859,25 +945,50 @@ class ConsolePrEdit(QTextEdit):
|
|
|
859
945
|
# display normal output. Exclude ConsolePrEdits
|
|
860
946
|
info = info if info else self.parseErrorHyperLinkInfo(msg)
|
|
861
947
|
filename = info.get("filename", "") if info else ""
|
|
948
|
+
|
|
949
|
+
# Determine if this is a workbox line of code, or code run directly
|
|
950
|
+
# in the console
|
|
951
|
+
isWorkbox = '<WorkboxSelection>' in filename or '<Workbox>' in filename
|
|
862
952
|
isConsolePrEdit = '<ConsolePrEdit>' in filename
|
|
863
953
|
|
|
954
|
+
# Starting in Python 3, tracebacks don't include the code executed
|
|
955
|
+
# for stdin, so workbox code won't appear. This attempts to include
|
|
956
|
+
# it.
|
|
957
|
+
if isWorkbox:
|
|
958
|
+
match = self.workbox_pattern.search(msg)
|
|
959
|
+
workboxName = match.groupdict().get("workboxName")
|
|
960
|
+
lineNum = int(match.groupdict().get("lineNum")) - 1
|
|
961
|
+
|
|
962
|
+
workboxLine = self.getWorkboxLine(workboxName, lineNum)
|
|
963
|
+
if workboxLine:
|
|
964
|
+
indent = self.getIndentForCodeTracebackLine(msg)
|
|
965
|
+
msg = "{}{}{}".format(msg, indent, workboxLine)
|
|
966
|
+
|
|
967
|
+
elif isConsolePrEdit:
|
|
968
|
+
consoleLine = self.consoleLine
|
|
969
|
+
indent = self.getIndentForCodeTracebackLine(msg)
|
|
970
|
+
msg = "{}{}{}\n".format(msg, indent, consoleLine)
|
|
971
|
+
|
|
864
972
|
# To make it easier to see relevant lines of a traceback, optionally insert
|
|
865
973
|
# a newline separating internal PrEditor code from the code run by user.
|
|
866
974
|
if self.addSepNewline:
|
|
867
975
|
if sepPreditorTrace:
|
|
868
|
-
msg
|
|
976
|
+
msg = "\n" + msg
|
|
869
977
|
self.addSepNewline = False
|
|
870
978
|
|
|
871
979
|
preditorCalls = ("cmdresult = e", "exec(compiled,")
|
|
872
980
|
if msg.strip().startswith(preditorCalls):
|
|
873
981
|
self.addSepNewline = True
|
|
874
982
|
|
|
983
|
+
# Error tracebacks and logging.stack_info supply msg's differently,
|
|
984
|
+
# so modify it here, so we get consistent results.
|
|
985
|
+
msg = msg.replace("\n\n", "\n")
|
|
986
|
+
|
|
875
987
|
if info and doHyperlink and not isConsolePrEdit:
|
|
876
988
|
fileStart = info.get("fileStart")
|
|
877
989
|
fileEnd = info.get("fileEnd")
|
|
878
990
|
lineNum = info.get("lineNum")
|
|
879
991
|
|
|
880
|
-
isWorkbox = '<WorkboxSelection>' in filename or '<Workbox>' in filename
|
|
881
992
|
if isWorkbox:
|
|
882
993
|
split = filename.split(':')
|
|
883
994
|
workboxIdx = split[-1]
|
preditor/gui/loggerwindow.py
CHANGED
|
@@ -1033,7 +1033,7 @@ class LoggerWindow(Window):
|
|
|
1033
1033
|
# Create timer to autohide status messages
|
|
1034
1034
|
self.statusTimer = QTimer()
|
|
1035
1035
|
self.statusTimer.setSingleShot(True)
|
|
1036
|
-
self.statusTimer.setInterval(
|
|
1036
|
+
self.statusTimer.setInterval(5000)
|
|
1037
1037
|
self.statusTimer.timeout.connect(self.clearStatusText)
|
|
1038
1038
|
|
|
1039
1039
|
def clearStatusText(self):
|
preditor/gui/workbox_mixin.py
CHANGED
|
@@ -118,7 +118,7 @@ class WorkboxMixin(object):
|
|
|
118
118
|
raise NotImplementedError("Mixin method not overridden.")
|
|
119
119
|
|
|
120
120
|
def __exec_selected__(self, truncate=True):
|
|
121
|
-
txt,
|
|
121
|
+
txt, lineNum = self.__selected_text__()
|
|
122
122
|
|
|
123
123
|
# Remove any leading white space shared across all lines
|
|
124
124
|
txt = textwrap.dedent(txt)
|
|
@@ -129,7 +129,7 @@ class WorkboxMixin(object):
|
|
|
129
129
|
# Make workbox line numbers match the workbox line numbers, by adding
|
|
130
130
|
# the appropriate number of newlines to mimic it's original position in
|
|
131
131
|
# the workbox.
|
|
132
|
-
txt = '\n' *
|
|
132
|
+
txt = '\n' * lineNum + txt
|
|
133
133
|
|
|
134
134
|
# execute the code
|
|
135
135
|
title = self.__workbox_trace_title__(selection=True)
|
preditor/gui/workboxwidget.py
CHANGED
|
@@ -207,7 +207,10 @@ class WorkboxWidget(WorkboxMixin, DocumentEditor):
|
|
|
207
207
|
|
|
208
208
|
@classmethod
|
|
209
209
|
def __write_file__(cls, filename, txt, encoding=None):
|
|
210
|
-
# Save unix newlines for simplicity
|
|
210
|
+
# Save unix newlines for simplicity. This should only be called for
|
|
211
|
+
# files which are not linked, so we don't inadvertently change a file's
|
|
212
|
+
# line-endings. For linked files, call saveAs, which bypasses this
|
|
213
|
+
# method writes without converting to unix line endings.
|
|
211
214
|
txt = cls.__unix_end_lines__(txt)
|
|
212
215
|
super(WorkboxWidget, cls).__write_file__(filename, txt, encoding=encoding)
|
|
213
216
|
|
|
@@ -72,9 +72,6 @@ class DocumentEditor(QsciScintilla):
|
|
|
72
72
|
fontsChanged = Signal(
|
|
73
73
|
QFont, QFont
|
|
74
74
|
) # emits the font size change (font size, margin font size)
|
|
75
|
-
documentSaved = Signal(
|
|
76
|
-
QsciScintilla, object
|
|
77
|
-
) # (DocumentEditor, filename) emitted when ever the document is saved.
|
|
78
75
|
|
|
79
76
|
def __init__(self, parent, filename='', lineno=0, delayable_engine='default'):
|
|
80
77
|
super(DocumentEditor, self).__init__(parent)
|
|
@@ -145,7 +142,7 @@ class DocumentEditor(QsciScintilla):
|
|
|
145
142
|
self.customContextMenuRequested.connect(self.showMenu)
|
|
146
143
|
self.selectionChanged.connect(self.updateSelectionInfo)
|
|
147
144
|
window = self.window()
|
|
148
|
-
if hasattr(window, '
|
|
145
|
+
if hasattr(window, 'styleSheetChanged'):
|
|
149
146
|
window.styleSheetChanged.connect(self.updateColorScheme)
|
|
150
147
|
|
|
151
148
|
# Create shortcuts
|
|
@@ -1179,8 +1176,6 @@ class DocumentEditor(QsciScintilla):
|
|
|
1179
1176
|
try:
|
|
1180
1177
|
txt = self.text()
|
|
1181
1178
|
WorkboxMixin.__write_file__(filename, txt, encoding=self._encoding)
|
|
1182
|
-
with open(filename, "w", encoding=self._encoding) as f:
|
|
1183
|
-
f.write(self.text())
|
|
1184
1179
|
except PermissionError as error:
|
|
1185
1180
|
logger.debug('An error occurred while saving')
|
|
1186
1181
|
QMessageBox.question(
|
|
@@ -1191,9 +1186,6 @@ class DocumentEditor(QsciScintilla):
|
|
|
1191
1186
|
)
|
|
1192
1187
|
return False
|
|
1193
1188
|
|
|
1194
|
-
# notify that the document was saved
|
|
1195
|
-
self.documentSaved.emit(self, filename)
|
|
1196
|
-
|
|
1197
1189
|
# update the file
|
|
1198
1190
|
if setFilename:
|
|
1199
1191
|
self.updateFilename(filename)
|
preditor/stream/director.py
CHANGED
|
@@ -6,11 +6,61 @@ import sys
|
|
|
6
6
|
from . import STDERR, STDOUT
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
class
|
|
9
|
+
class _DirectorBuffer(io.RawIOBase):
|
|
10
|
+
"""Binary buffer that forwards text writes to the manager.
|
|
11
|
+
|
|
12
|
+
This makes the stream more compatible including if enabled when running tox.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
manager (Manager): The manager that writes are stored in.
|
|
16
|
+
state: The state passed to the manager. This is often ``preditor.stream.STDOUT``
|
|
17
|
+
or ``preditor.stream.STDERR``.
|
|
18
|
+
old_stream: A second stream that will be written to every time this stream
|
|
19
|
+
is written to. This allows this object to replace sys.stdout and still
|
|
20
|
+
send that output to the original stdout, which is useful for not breaking
|
|
21
|
+
DCC's script editors. Pass False to disable this feature. If you pass None
|
|
22
|
+
and state is set to ``preditor.stream.STDOUT`` or ``preditor.stream.STDERR``
|
|
23
|
+
this will automatically be set to the current sys.stdout or sys.stderr.
|
|
24
|
+
name (str, optional): Stored on self.name.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, manager, state, old_stream=None, name='nul'):
|
|
28
|
+
super().__init__()
|
|
29
|
+
self.manager = manager
|
|
30
|
+
self.state = state
|
|
31
|
+
self.old_stream = old_stream
|
|
32
|
+
self.name = name
|
|
33
|
+
|
|
34
|
+
def flush(self):
|
|
35
|
+
if self.old_stream:
|
|
36
|
+
self.old_stream.flush()
|
|
37
|
+
super().flush()
|
|
38
|
+
|
|
39
|
+
def writable(self):
|
|
40
|
+
return True
|
|
41
|
+
|
|
42
|
+
def write(self, b):
|
|
43
|
+
if isinstance(b, memoryview):
|
|
44
|
+
b = b.tobytes()
|
|
45
|
+
|
|
46
|
+
# Decode incoming bytes (TextIOWrapper encodes before sending here)
|
|
47
|
+
msg = b.decode("utf-8", errors="replace")
|
|
48
|
+
self.manager.write(msg, self.state)
|
|
49
|
+
|
|
50
|
+
if self.old_stream:
|
|
51
|
+
self.old_stream.write(msg)
|
|
52
|
+
|
|
53
|
+
return len(b)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class Director(io.TextIOWrapper):
|
|
10
57
|
"""A file like object that stores the text written to it in a manager.
|
|
11
58
|
This manager can be shared between multiple Directors to build a single
|
|
12
59
|
continuous history of all writes.
|
|
13
60
|
|
|
61
|
+
While this uses a buffer under the hood, buffering is disabled and any calls
|
|
62
|
+
to write will automatically flush the buffer.
|
|
63
|
+
|
|
14
64
|
Args:
|
|
15
65
|
manager (Manager): The manager that writes are stored in.
|
|
16
66
|
state: The state passed to the manager. This is often ``preditor.stream.STDOUT``
|
|
@@ -24,14 +74,11 @@ class Director(io.TextIOBase):
|
|
|
24
74
|
"""
|
|
25
75
|
|
|
26
76
|
def __init__(self, manager, state, old_stream=None, *args, **kwargs):
|
|
27
|
-
super(Director, self).__init__(*args, **kwargs)
|
|
28
|
-
self.manager = manager
|
|
29
|
-
self.state = state
|
|
30
|
-
|
|
31
77
|
# Keep track of whether we wrapped a std stream
|
|
32
78
|
# that way we don't .close() any streams that we don't control
|
|
33
79
|
self.std_stream_wrapped = False
|
|
34
80
|
|
|
81
|
+
name = 'nul'
|
|
35
82
|
if old_stream is False:
|
|
36
83
|
old_stream = None
|
|
37
84
|
elif old_stream is None:
|
|
@@ -39,15 +86,28 @@ class Director(io.TextIOBase):
|
|
|
39
86
|
# On Windows if we're in pythonw.exe, then sys.stdout is named "nul"
|
|
40
87
|
# And it uses cp1252 encoding (which breaks with unicode)
|
|
41
88
|
# So if we find this nul TextIOWrapper, it's safe to just skip it
|
|
42
|
-
|
|
89
|
+
name = getattr(sys.stdout, 'name', '')
|
|
90
|
+
if name != 'nul':
|
|
43
91
|
self.std_stream_wrapped = True
|
|
44
92
|
old_stream = sys.stdout
|
|
45
93
|
elif state == STDERR:
|
|
46
|
-
|
|
94
|
+
name = getattr(sys.stderr, 'name', '')
|
|
95
|
+
if name != 'nul':
|
|
47
96
|
self.std_stream_wrapped = True
|
|
48
97
|
old_stream = sys.stderr
|
|
49
98
|
|
|
50
99
|
self.old_stream = old_stream
|
|
100
|
+
self.manager = manager
|
|
101
|
+
self.state = state
|
|
102
|
+
|
|
103
|
+
# Build the buffer. This provides the expected interface for tox, etc.
|
|
104
|
+
raw = _DirectorBuffer(manager, state, old_stream, name)
|
|
105
|
+
buffer = io.BufferedWriter(raw)
|
|
106
|
+
|
|
107
|
+
super().__init__(buffer, encoding="utf-8", write_through=True, *args, **kwargs)
|
|
108
|
+
|
|
109
|
+
def __repr__(self):
|
|
110
|
+
return f"<Director state={self.state} old_stream={self.old_stream!r}>"
|
|
51
111
|
|
|
52
112
|
def close(self):
|
|
53
113
|
if (
|
|
@@ -58,16 +118,27 @@ class Director(io.TextIOBase):
|
|
|
58
118
|
):
|
|
59
119
|
self.old_stream.close()
|
|
60
120
|
|
|
61
|
-
super(
|
|
121
|
+
super().close()
|
|
62
122
|
|
|
63
|
-
def
|
|
64
|
-
|
|
65
|
-
|
|
123
|
+
def write(self, msg):
|
|
124
|
+
super().write(msg)
|
|
125
|
+
# Force a write of any buffered data
|
|
126
|
+
self.flush()
|
|
66
127
|
|
|
67
|
-
|
|
128
|
+
# These methods enable terminal features like color coding etc.
|
|
129
|
+
def isatty(self):
|
|
130
|
+
if self.old_stream is not None:
|
|
131
|
+
return self.old_stream.isatty()
|
|
132
|
+
return False
|
|
68
133
|
|
|
69
|
-
|
|
70
|
-
|
|
134
|
+
@property
|
|
135
|
+
def encoding(self):
|
|
136
|
+
if self.old_stream is not None:
|
|
137
|
+
return self.old_stream.encoding
|
|
138
|
+
return super().encoding
|
|
71
139
|
|
|
72
|
-
|
|
73
|
-
|
|
140
|
+
@property
|
|
141
|
+
def errors(self):
|
|
142
|
+
if self.old_stream is not None:
|
|
143
|
+
return self.old_stream.errors
|
|
144
|
+
return super().errors
|
preditor/version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '1.
|
|
32
|
-
__version_tuple__ = version_tuple = (1,
|
|
31
|
+
__version__ = version = '1.5.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (1, 5, 0)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'gb7045f3af'
|
|
@@ -4,7 +4,7 @@ preditor/about_module.py,sha256=_jhd2vaWArnnw2qvsZtOSQvntec9PG_PCiSFbcS1j_I,5347
|
|
|
4
4
|
preditor/cli.py,sha256=kyVr0V43KYSMLIEWXH54CA0ByFmPcpbFF-8cli6PVow,5300
|
|
5
5
|
preditor/config.py,sha256=DTDqJ8cVnbvAE8IytAREMV7HJS2b3DLH1jtFIkdTo8c,11883
|
|
6
6
|
preditor/contexts.py,sha256=xxRbOFORsvlG___EuSiKuT1aiKJvnkFojFZFZE0HPNk,5147
|
|
7
|
-
preditor/debug.py,sha256=
|
|
7
|
+
preditor/debug.py,sha256=9BkNis3l4gockQJpblxe6jnh6OyMITKzY3WKd035vGI,4625
|
|
8
8
|
preditor/enum.py,sha256=snG5V259dH9SI1kwBqZeZdhat36F1iAmIjYErosnDUg,23489
|
|
9
9
|
preditor/excepthooks.py,sha256=UZ2PsxUo8hA_PBabAXsQkiwKgTLgY_3lueFymIb0V50,5127
|
|
10
10
|
preditor/logging_config.py,sha256=bGRCaSq6ugPG2L5keTpw5CNiQwi9PFDxDMDhx_VLHsg,1583
|
|
@@ -13,7 +13,7 @@ preditor/plugins.py,sha256=RAeyvdZxQsZY313ae5aAhahqpjuhaXJzDxPyLcR-09U,4401
|
|
|
13
13
|
preditor/prefs.py,sha256=BPtSsdv2yuiRpIaqEml9fxlVYKHNfqQ77hp5YIQRDBg,2172
|
|
14
14
|
preditor/settings.py,sha256=DV9_DbJorEnhdIvW15E7h7PswlQUsy0UlA8bXUYN0og,2206
|
|
15
15
|
preditor/streamhandler_helper.py,sha256=kiU6T9WqJ3JKTTKCa7IUU8brwK7zO5UUpEzLhEfKe44,1788
|
|
16
|
-
preditor/version.py,sha256=
|
|
16
|
+
preditor/version.py,sha256=9Si62UWsEQhvgE6BKzBJWtlwXjMhsCJF_E0gnxJgjEU,712
|
|
17
17
|
preditor/weakref.py,sha256=b--KomFAHcMWr3DEAIN2j3XxRhjDWKw0WABXyn1nxDg,12177
|
|
18
18
|
preditor/cores/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
19
|
preditor/cores/core.py,sha256=mdVXlEPDh8kIdOpMDqi8P1VaqfVvyu5cXBq_toONcaM,586
|
|
@@ -32,7 +32,7 @@ preditor/gui/__init__.py,sha256=DeOH5K4_M0YLNx1i7NRvwCgwjyjkre4vVwlr5OEMx9Q,3423
|
|
|
32
32
|
preditor/gui/app.py,sha256=EhVsVMr5C5WSKNDlxGawzg-3lbTJxDMLi2fPomECacM,5869
|
|
33
33
|
preditor/gui/codehighlighter.py,sha256=wUOgLDhcAQf72DSS65r9_RYdv6PVJwWFXTdOPvvps_A,6790
|
|
34
34
|
preditor/gui/completer.py,sha256=U2FBJA_ceFq_viGqPogncH-6vhrtTcpxAF61SUnaOJ8,7813
|
|
35
|
-
preditor/gui/console.py,sha256=
|
|
35
|
+
preditor/gui/console.py,sha256=It-cXB91kwtbmkkkTwZ78fi3jt4SnHQDId5GmsyULtY,40765
|
|
36
36
|
preditor/gui/dialog.py,sha256=fasmBDhN-B6uretbSQ3x1SxImsQLSKIMw4PC9B8BimI,6535
|
|
37
37
|
preditor/gui/drag_tab_bar.py,sha256=EgEXdV4odNj_1_YiGP2kmpGKUaBPe75JTwudU8Xj-Gw,8113
|
|
38
38
|
preditor/gui/editor_chooser.py,sha256=lv1eY0UJOulX1l-P-ZQEoneYz6BNX2VkXEbg3GUu1ag,1991
|
|
@@ -41,15 +41,15 @@ preditor/gui/find_files.py,sha256=TtsjHXKWvwKrxHYZryB_NPANoFpMRlAxWJp1hA6Ny2I,43
|
|
|
41
41
|
preditor/gui/level_buttons.py,sha256=h0pMMzp-ElwcN_qfR4EBQtXn1C666KVcnR9dMoixD3E,12597
|
|
42
42
|
preditor/gui/logger_window_handler.py,sha256=43-DxzuNJq6UV6Ofc4TKRt179Sf5AxDkVDGrtq_Knok,1571
|
|
43
43
|
preditor/gui/logger_window_plugin.py,sha256=Lj9Q8L82GjzZ21ZI5W0FCHhrPHNALIunT_C9ua15l2w,1272
|
|
44
|
-
preditor/gui/loggerwindow.py,sha256=
|
|
44
|
+
preditor/gui/loggerwindow.py,sha256=PaTTa_vTXDAjEWxwPBEtTosGppu9S38wI3zOUy7MGSI,54458
|
|
45
45
|
preditor/gui/newtabwidget.py,sha256=5aCWn9xAl5h1oZACqVuEsOAbzKTS2RegrLI41gROC8A,1971
|
|
46
46
|
preditor/gui/set_text_editor_path_dialog.py,sha256=bn8IeQHXPSU4PLYXQmSxKB1V8iuJaI1PA5aDJNgxXks,2292
|
|
47
47
|
preditor/gui/status_label.py,sha256=SlNRmPc28S4E2OFVmErv0DmZstk4011Q_7uLp43SF0A,3320
|
|
48
48
|
preditor/gui/suggest_path_quotes_dialog.py,sha256=QUJf_9hs8wOO6bFOr8_Z2xnNhSA8TfKFMOzheUqUDnY,1822
|
|
49
49
|
preditor/gui/window.py,sha256=6Ztjqb0Xfh8DsI7hBF4Rg-4-gWPZKTV4GRJiz3V8O6Q,5904
|
|
50
|
-
preditor/gui/workbox_mixin.py,sha256=
|
|
50
|
+
preditor/gui/workbox_mixin.py,sha256=_XrivnyBOjnjHE35WrDXGJbwxP0vFkC2avhypLOHd_U,18562
|
|
51
51
|
preditor/gui/workbox_text_edit.py,sha256=wrJcGqIJ2-5BqEcP_qVKNlWUD8Geo8IxrIXncnDG6yw,4472
|
|
52
|
-
preditor/gui/workboxwidget.py,sha256
|
|
52
|
+
preditor/gui/workboxwidget.py,sha256=-u4DiwgJ6Oc_WjeWSny5HKu4KfJY7uQ0wUftnuH9R7Q,10840
|
|
53
53
|
preditor/gui/fuzzy_search/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
54
54
|
preditor/gui/fuzzy_search/fuzzy_search.py,sha256=zohr6oAE99FZFlLjqKZajBrdyJIYLW96rcCOy5Bn1i0,3714
|
|
55
55
|
preditor/gui/group_tab_widget/__init__.py,sha256=EalGlpDcinbI6WdwmG7BK94519rUHfzxA0qez-o4m6k,13637
|
|
@@ -116,7 +116,7 @@ preditor/resource/lang/python.json,sha256=CXiQh0jcgd-OCrM-s9IF7s4o-g5WRA4vDaAyTR
|
|
|
116
116
|
preditor/resource/stylesheet/Bright.css,sha256=SfnPRBqfqEL4IF8RGt9PpLX_cSiGpfG9Q2hE1F9uTsk,2480
|
|
117
117
|
preditor/resource/stylesheet/Dark.css,sha256=Sige7beBhRyfav9_mXvn-LhksxbX0_WEgpZSKhmPavc,5384
|
|
118
118
|
preditor/scintilla/__init__.py,sha256=IDndbcS8KW1TMcQTA1Vd-LzcdsNtOUfyldnjkKae0yw,956
|
|
119
|
-
preditor/scintilla/documenteditor.py,sha256=
|
|
119
|
+
preditor/scintilla/documenteditor.py,sha256=sLdVtRsq63LZEd4_Bmpn5JEAmTpBgFG_M4MZUxfz6yA,81129
|
|
120
120
|
preditor/scintilla/finddialog.py,sha256=WxNVvkCfXrw_8E3-7tS1DIS9H1-ioLASDgIbniDxz3g,2376
|
|
121
121
|
preditor/scintilla/delayables/__init__.py,sha256=lj9tMc3IL2QeaN858Ixt_n7clJngbKqG2sk66vIcFcQ,275
|
|
122
122
|
preditor/scintilla/delayables/smart_highlight.py,sha256=5emI4StsQCIYizoFsib1rdw0gnUarV9TQ6CwxmzXXpU,3623
|
|
@@ -151,17 +151,17 @@ preditor/scintilla/lexers/mulexer.py,sha256=tlyiKYBDRpmxcrH9FGi-Ogz2alHPhyqNaqVr
|
|
|
151
151
|
preditor/scintilla/lexers/pythonlexer.py,sha256=dh_wSBhhYIIV1hUF_0cWbZCdMBMsOB6EY6x8valWo1s,1737
|
|
152
152
|
preditor/scintilla/ui/finddialog.ui,sha256=CEXouVKqVCDOfMBciK-89OmsGe2Kr6XdMbSPiffp0PA,4048
|
|
153
153
|
preditor/stream/__init__.py,sha256=SxILA3U3W8aNfVBd_oJ4-WXkA214cl1zGLSCelFx5QU,2682
|
|
154
|
-
preditor/stream/director.py,sha256=
|
|
154
|
+
preditor/stream/director.py,sha256=UIGhLoZPCMHlDT04CPbrm4aTzggd4A9wR2Fitu2LT08,5296
|
|
155
155
|
preditor/stream/manager.py,sha256=NP4lf6hd5L_Ui-9Ws66gVEk6ZL8YqF7BxOzUsdrh3v0,2811
|
|
156
156
|
preditor/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
157
157
|
preditor/utils/cute.py,sha256=tLTChQ1-zMzHHaKpG85u4FbtJl_2R1E8-uckpOQbEEM,1057
|
|
158
158
|
preditor/utils/stylesheets.py,sha256=EVWZNq3WnaRiyUPoYMKQo_dLEwbRyKu26b03I1JDA-s,1622
|
|
159
159
|
preditor/utils/text_search.py,sha256=21kuSDTpLIPUcriB81WP1kWfzuDBuP13ZtHUtypP5jE,14218
|
|
160
|
-
preditor-1.
|
|
160
|
+
preditor-1.5.0.dist-info/licenses/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
|
|
161
161
|
tests/find_files/test_find_files.py,sha256=ETVe1tC666uTc_5pPhjzPCaMrMkYUB8iKGXDfW9usgI,2722
|
|
162
162
|
tests/ide/test_delayable_engine.py,sha256=Nv6SQXpwIdfgRbEOsEcR-7jhjZ6E0THPdwDoPvAM6y4,5948
|
|
163
|
-
preditor-1.
|
|
164
|
-
preditor-1.
|
|
165
|
-
preditor-1.
|
|
166
|
-
preditor-1.
|
|
167
|
-
preditor-1.
|
|
163
|
+
preditor-1.5.0.dist-info/METADATA,sha256=GsCQMiLBp5l5PPuC3pq34mqNgg_DFvlIA2f6fAwWQfI,12744
|
|
164
|
+
preditor-1.5.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
165
|
+
preditor-1.5.0.dist-info/entry_points.txt,sha256=mpe0HFD_oIEBNPTJNyUEbmMV6Ivrp4EuYyZ34C5d_PU,519
|
|
166
|
+
preditor-1.5.0.dist-info/top_level.txt,sha256=dZSBDecBQovRyqbFdvwk1AvMon636dJ14vr9ov1LSPs,20
|
|
167
|
+
preditor-1.5.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|