Skip to content

Commit c343024

Browse files
Merge 1f1b35b into ac5ab40
2 parents ac5ab40 + 1f1b35b commit c343024

2 files changed

Lines changed: 100 additions & 0 deletions

File tree

source/globalCommands.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,6 +1540,42 @@ def script_navigatorObject_devInfo(self,gesture):
15401540
script_navigatorObject_devInfo.__doc__ = _("Logs information about the current navigator object which is useful to developers and activates the log viewer so the information can be examined.")
15411541
script_navigatorObject_devInfo.category=SCRCAT_TOOLS
15421542

1543+
@script(
1544+
# Translators: Input help mode message for a command to delimit then
1545+
# copy a fragment of the log to clipboard
1546+
description=_(
1547+
"Mark the current end of the log as the start of the fragment to be"
1548+
" copied to clipboard by pressing again."
1549+
),
1550+
category=SCRCAT_TOOLS,
1551+
gesture="kb:NVDA+control+shift+f1"
1552+
)
1553+
def script_log_markStartThenCopy(self, gesture):
1554+
if globalVars.appArgs.secure:
1555+
return
1556+
if log.fragmentStart is None:
1557+
if log.markFragmentStart():
1558+
# Translators: Message when marking the start of a fragment of the log file for later copy
1559+
# to clipboard
1560+
ui.message(_("Log fragment start position marked, press again to copy to clipboard"))
1561+
else:
1562+
# Translators: Message when failed to mark the start of a
1563+
# fragment of the log file for later copy to clipboard
1564+
ui.message(_("Unable to mark log position"))
1565+
return
1566+
text = log.getFragment()
1567+
if not text:
1568+
# Translators: Message when attempting to copy an empty fragment of the log file
1569+
ui.message(_("No new log entry to copy"))
1570+
return
1571+
if api.copyToClip(text):
1572+
# Translators: Message when a fragment of the log file has been
1573+
# copied to clipboard
1574+
ui.message(_("Log fragment copied to clipboard"))
1575+
else:
1576+
# Translators: Presented when unable to copy to the clipboard because of an error.
1577+
ui.message(_("Unable to copy"))
1578+
15431579
def script_toggleProgressBarOutput(self,gesture):
15441580
outputMode=config.conf["presentation"]["progressBarUpdates"]["progressBarOutputMode"]
15451581
if outputMode=="both":

source/logHandler.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ class Logger(logging.Logger):
113113
DEBUGWARNING = 15
114114
OFF = 100
115115

116+
#: The start position of a fragment of the log file as marked with
117+
#: L{markFragmentStart} for later retrieval using L{getFragment}.
118+
#: @type: C{long}
119+
fragmentStart = None
120+
116121
def _log(self, level, msg, args, exc_info=None, extra=None, codepath=None, activateLogViewer=False, stack_info=None):
117122
if not extra:
118123
extra={}
@@ -189,6 +194,65 @@ def exception(self, msg="", exc_info=True, **kwargs):
189194
return
190195
self._log(level, msg, (), exc_info=exc_info, **kwargs)
191196

197+
def markFragmentStart(self):
198+
"""Mark the current end of the log file as the start position of a
199+
fragment to be later retrieved by L{getFragment}.
200+
@returns: Whether a log file is in use and a position could be marked
201+
@rtype: bool
202+
"""
203+
if (
204+
not globalVars.appArgs
205+
or not globalVars.appArgs.logFileName
206+
or not self.handlers
207+
or not isinstance(self.handlers[0], FileHandler)
208+
):
209+
return False
210+
import codecs
211+
try:
212+
f = codecs.open(globalVars.appArgs.logFileName, "r", encoding="UTF-8")
213+
# io.IOBase.seek: whence=2 -- end of stream
214+
f.seek(0, whence=2)
215+
self.fragmentStart = f.tell()
216+
return True
217+
except: # IOError is the most expected. Catch-all anyway.
218+
self.exception()
219+
return False
220+
finally:
221+
try:
222+
f.close()
223+
except:
224+
pass
225+
226+
def getFragment(self):
227+
"""Retrieve a fragment of the log starting from the position marked using
228+
L{markFragmentStart}.
229+
If L{fragmentStart} does not point to the current end of the log file, it
230+
is reset to C{None} after reading the fragment.
231+
@returns: The text of the fragment, or C{None} if L{fragmentStart} is None.
232+
@rtype: str
233+
"""
234+
if (
235+
not globalVars.appArgs
236+
or globalVars.appArgs.secure
237+
or self.fragmentStart is None
238+
):
239+
return None
240+
import codecs
241+
try:
242+
f = codecs.open(globalVars.appArgs.logFileName, "r", encoding="UTF-8")
243+
f.seek(self.fragmentStart)
244+
fragment = f.read()
245+
if fragment:
246+
self.fragmentStart = None
247+
return fragment
248+
except: # IOError is the most expected. Catch-all anyway.
249+
self.exception()
250+
finally:
251+
try:
252+
f.close()
253+
except:
254+
pass
255+
192256
class RemoteHandler(logging.Handler):
193257

194258
def __init__(self):

0 commit comments

Comments
 (0)