mirror of
https://github.com/clearml/wexpect-venv
synced 2025-05-31 18:49:18 +00:00
[UDT] updated wexpect with a working one
This commit is contained in:
parent
f946cbf8e1
commit
d6a8cc5e57
20
README.md
20
README.md
@ -2,6 +2,7 @@
|
||||
|
||||
wexpect is a Windows alternative of [pexpect](https://pexpect.readthedocs.io/en/stable/).
|
||||
|
||||
---
|
||||
## pexpect
|
||||
|
||||
Pexpect is a Python module for spawning child applications and controlling
|
||||
@ -36,6 +37,7 @@ For example::
|
||||
This works even for commands that ask for passwords or other input outside of
|
||||
the normal stdio streams.
|
||||
|
||||
---
|
||||
## Wexpect
|
||||
|
||||
Wexpect is a one-file code developed at University of Washington. There are several copy of this code,
|
||||
@ -47,16 +49,28 @@ Here are some useful links:
|
||||
|
||||
This repo tries to fix these limitations.
|
||||
|
||||
---
|
||||
## Installation and limitation of wexpect
|
||||
|
||||
Current version does *not* work on python-3.x. You need to use **python 2.x** to use wexpect.
|
||||
|
||||
One (non stanbdard) package, **pypiwin32** needed to use wexpect.
|
||||
### Standard installation
|
||||
|
||||
This version is uploaded to pypi server so you can easily install with pip:
|
||||
|
||||
pip install wexpect
|
||||
|
||||
### Manual installation
|
||||
|
||||
Because this is a tiny project dropping the wexpect.py file into your working directory is usually
|
||||
good enough instead of installing. However in this case you need to install manually the one dependence.
|
||||
|
||||
One (non stanbdard) package, **pypiwin32** is needed by wexpect.
|
||||
|
||||
pip install pypiwin32
|
||||
|
||||
Dropping the wexpect.py file into your working directory is usually good enough instead of installing.
|
||||
|
||||
|
||||
---
|
||||
## Usage
|
||||
|
||||
See pexpect examples for usage.
|
||||
|
401
wexpect.py
401
wexpect.py
@ -82,8 +82,12 @@ try:
|
||||
import resource
|
||||
import fcntl
|
||||
else:
|
||||
from StringIO import StringIO
|
||||
try:
|
||||
from ctypes import windll
|
||||
import pywintypes
|
||||
from win32com.shell.shellcon import CSIDL_APPDATA
|
||||
from win32com.shell.shell import SHGetSpecialFolderPath
|
||||
from win32console import *
|
||||
from win32process import *
|
||||
from win32con import *
|
||||
@ -93,6 +97,8 @@ try:
|
||||
import winerror
|
||||
except ImportError, e:
|
||||
raise ImportError(str(e) + "\nThis package requires the win32 python packages.")
|
||||
screenbufferfillchar = u'\4'
|
||||
maxconsoleY = 8000
|
||||
except ImportError, e:
|
||||
raise ImportError (str(e) + """
|
||||
|
||||
@ -274,9 +280,23 @@ def run (command, timeout=-1, withexitstatus=False, events=None, extra_args=None
|
||||
else:
|
||||
return child_result
|
||||
|
||||
def spawn(command, args=[], timeout=30, maxread=2000, searchwindowsize=None, logfile=None, cwd=None, env=None):
|
||||
def spawn(command, args=[], timeout=30, maxread=2000, searchwindowsize=None, logfile=None, cwd=None, env=None,
|
||||
codepage=None):
|
||||
log('=' * 80)
|
||||
log('Buffer size: %s' % maxread)
|
||||
if searchwindowsize:
|
||||
log('Search window size: %s' % searchwindowsize)
|
||||
log('Timeout: %ss' % timeout)
|
||||
if env:
|
||||
log('Environment:')
|
||||
for name in env:
|
||||
log('\t%s=%s' % (name, env[name]))
|
||||
if cwd:
|
||||
log('Working directory: %s' % cwd)
|
||||
log('Spawning %s' % join_args([command] + args))
|
||||
if sys.platform == 'win32':
|
||||
return spawn_windows(command, args, timeout, maxread, searchwindowsize, logfile, cwd, env)
|
||||
return spawn_windows(command, args, timeout, maxread, searchwindowsize, logfile, cwd, env,
|
||||
codepage)
|
||||
else:
|
||||
return spawn_unix(command, args, timeout, maxread, searchwindowsize, logfile, cwd, env)
|
||||
|
||||
@ -1543,7 +1563,10 @@ class spawn_unix (object):
|
||||
while self.isalive():
|
||||
r,w,e = self.__select([self.child_fd, self.STDIN_FILENO], [], [])
|
||||
if self.child_fd in r:
|
||||
data = self.__interact_read(self.child_fd)
|
||||
try:
|
||||
data = self.__interact_read(self.child_fd)
|
||||
except OSError, e:
|
||||
break
|
||||
if output_filter: data = output_filter(data)
|
||||
if self.logfile is not None:
|
||||
self.logfile.write (data)
|
||||
@ -1608,7 +1631,8 @@ class spawn_windows (spawn_unix, object):
|
||||
"""This is the main class interface for Pexpect. Use this class to start
|
||||
and control child applications. """
|
||||
|
||||
def __init__(self, command, args=[], timeout=30, maxread=60000, searchwindowsize=None, logfile=None, cwd=None, env=None):
|
||||
def __init__(self, command, args=[], timeout=30, maxread=60000, searchwindowsize=None, logfile=None, cwd=None, env=None,
|
||||
codepage=None):
|
||||
self.stdin = sys.stdin
|
||||
self.stdout = sys.stdout
|
||||
self.stderr = sys.stderr
|
||||
@ -1643,6 +1667,7 @@ class spawn_windows (spawn_unix, object):
|
||||
self.ocwd = os.getcwdu()
|
||||
self.cwd = cwd
|
||||
self.env = env
|
||||
self.codepage = codepage
|
||||
|
||||
# allow dummy instances for subclasses that may not use command or args.
|
||||
if command is None:
|
||||
@ -1701,12 +1726,20 @@ class spawn_windows (spawn_unix, object):
|
||||
|
||||
self.name = '<' + ' '.join (self.args) + '>'
|
||||
|
||||
self.wtty = Wtty()
|
||||
#assert self.pid is None, 'The pid member should be None.'
|
||||
#assert self.command is not None, 'The command member should not be None.'
|
||||
|
||||
self.wtty = Wtty(codepage=self.codepage)
|
||||
|
||||
if self.cwd is not None:
|
||||
os.chdir(self.cwd)
|
||||
|
||||
self.child_fd = self.wtty.spawn(self.command, self.args, self.env)
|
||||
|
||||
if self.cwd is not None:
|
||||
# Restore the original working dir
|
||||
os.chdir(self.ocwd)
|
||||
|
||||
self.terminated = False
|
||||
self.closed = False
|
||||
self.pid = self.wtty.pid
|
||||
@ -1759,20 +1792,20 @@ class spawn_windows (spawn_unix, object):
|
||||
available right away then one character will be returned immediately.
|
||||
It will not wait for 30 seconds for another 99 characters to come in.
|
||||
|
||||
This is a wrapper around wtty.read(). It uses select.select() to
|
||||
implement the timeout. """
|
||||
This is a wrapper around Wtty.read(). """
|
||||
|
||||
|
||||
if self.closed:
|
||||
raise ValueError ('I/O operation on closed file in read_nonblocking().')
|
||||
|
||||
if not self.wtty.isalive():
|
||||
self.flag_eof = True
|
||||
if timeout is None:
|
||||
# Do not raise TIMEOUT because we might be waiting for EOF
|
||||
# sleep to keep CPU utilization down
|
||||
time.sleep(.05)
|
||||
else:
|
||||
raise TIMEOUT ('Timeout exceeded in read_nonblocking().')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if timeout == -1:
|
||||
timeout = self.timeout
|
||||
@ -1780,7 +1813,15 @@ class spawn_windows (spawn_unix, object):
|
||||
s = self.wtty.read_nonblocking(timeout, size)
|
||||
|
||||
if s == '':
|
||||
raise TIMEOUT ('Timeout exceeded in read_nonblocking().')
|
||||
if not self.wtty.isalive():
|
||||
self.flag_eof = True
|
||||
raise EOF('End Of File (EOF) in read_nonblocking().')
|
||||
if timeout is None:
|
||||
# Do not raise TIMEOUT because we might be waiting for EOF
|
||||
# sleep to keep CPU utilization down
|
||||
time.sleep(.05)
|
||||
else:
|
||||
raise TIMEOUT ('Timeout exceeded in read_nonblocking().')
|
||||
|
||||
if self.logfile is not None:
|
||||
self.logfile.write (s)
|
||||
@ -1851,7 +1892,7 @@ class spawn_windows (spawn_unix, object):
|
||||
|
||||
if not self.isalive():
|
||||
raise ExceptionPexpect ('Cannot wait for dead child process.')
|
||||
|
||||
|
||||
# We can't use os.waitpid under Windows because of 'permission denied'
|
||||
# exception? Perhaps if not running as admin (or UAC enabled under
|
||||
# Vista/7). Simply loop and wait for child to exit.
|
||||
@ -1903,23 +1944,31 @@ class spawn_windows (spawn_unix, object):
|
||||
|
||||
class Wtty:
|
||||
|
||||
def __init__(self, timeout=30):
|
||||
def __init__(self, timeout=30, codepage=None):
|
||||
self.__buffer = StringIO()
|
||||
self.__bufferY = 0
|
||||
self.__currentReadCo = PyCOORDType(0, 0)
|
||||
self.__consSize = [80, 16000]
|
||||
self.__parentPid = 0
|
||||
self.__oproc = 0
|
||||
self.__opid = 0
|
||||
self.conpid = 0
|
||||
self.__otid = 0
|
||||
self.__switch = True
|
||||
self.__childProcess = None
|
||||
self.codepage = codepage
|
||||
self.console = False
|
||||
self.lastRead = 0
|
||||
self.lastReadData = ""
|
||||
self.pid = None
|
||||
self.processList = []
|
||||
# We need a timeout for connecting to the child process
|
||||
self.timeout = timeout
|
||||
self.totalRead = 0
|
||||
|
||||
def spawn(self, command, args=[], env=None):
|
||||
"""Spawns spawner.py with correct arguments."""
|
||||
|
||||
ts = time.time()
|
||||
self.startChild(args, env)
|
||||
|
||||
while True:
|
||||
@ -1942,7 +1991,7 @@ class Wtty:
|
||||
if not self.__childProcess:
|
||||
raise ExceptionPexpect ('The process ' + args[0] + ' could not be started.')
|
||||
|
||||
self.__childProcess = win32api.OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, 0, childPid)
|
||||
|
||||
|
||||
winHandle = int(GetConsoleWindow())
|
||||
|
||||
@ -1971,6 +2020,11 @@ class Wtty:
|
||||
dirname = os.path.dirname(sys.executable
|
||||
if getattr(sys, 'frozen', False) else
|
||||
os.path.abspath(__file__))
|
||||
if getattr(sys, 'frozen', False):
|
||||
logdir = os.path.splitext(sys.executable)[0]
|
||||
else:
|
||||
logdir = dirname
|
||||
logdir = os.path.basename(logdir)
|
||||
spath = [dirname]
|
||||
pyargs = ['-c']
|
||||
if getattr(sys, 'frozen', False):
|
||||
@ -1979,10 +2033,17 @@ class Wtty:
|
||||
# py2exe: Needs appropriate 'zipfile' option in setup script and
|
||||
# 'bundle_files' 3
|
||||
spath.append(os.path.join(dirname, 'library.zip'))
|
||||
spath.append(os.path.join(dirname, 'lib', 'library.zip'))
|
||||
spath.append(os.path.join(dirname, 'library.zip',
|
||||
os.path.basename(os.path.splitext(sys.executable)[0])))
|
||||
if os.path.isdir(os.path.join(dirname, 'lib')):
|
||||
dirname = os.path.join(dirname, 'lib')
|
||||
spath.append(os.path.join(dirname, 'library.zip'))
|
||||
spath.append(os.path.join(dirname, 'library.zip',
|
||||
os.path.basename(os.path.splitext(sys.executable)[0])))
|
||||
pyargs.insert(0, '-S') # skip 'import site'
|
||||
pid = GetCurrentProcessId()
|
||||
tid = win32api.GetCurrentThreadId()
|
||||
cp = self.codepage or windll.kernel32.GetACP()
|
||||
# If we are running 'frozen', expect python.exe in the same directory
|
||||
# as the packed executable.
|
||||
# py2exe: The python executable can be included via setup script by
|
||||
@ -1993,9 +2054,10 @@ class Wtty:
|
||||
' '.join(pyargs),
|
||||
"import sys; sys.path = %r + sys.path;"
|
||||
"args = %r; import wexpect;"
|
||||
"wexpect.ConsoleReader(wexpect.join_args(args), %i, %i)" % (spath, args, pid, tid))
|
||||
"wexpect.ConsoleReader(wexpect.join_args(args), %i, %i, cp=%i, logdir=%r)" % (spath, args, pid, tid, cp, logdir))
|
||||
|
||||
self.__oproc, _, self.__opid, self.__otid = CreateProcess(None, commandLine, None, None, False,
|
||||
|
||||
self.__oproc, _, self.conpid, self.__otid = CreateProcess(None, commandLine, None, None, False,
|
||||
CREATE_NEW_CONSOLE, env, None, si)
|
||||
|
||||
|
||||
@ -2010,19 +2072,24 @@ class Wtty:
|
||||
FreeConsole()
|
||||
|
||||
try:
|
||||
AttachConsole(self.__opid)
|
||||
AttachConsole(self.conpid)
|
||||
self.__consin = GetStdHandle(STD_INPUT_HANDLE)
|
||||
self.__consout = self.getConsoleOut()
|
||||
except Exception, e:
|
||||
#e = traceback.format_exc()
|
||||
try:
|
||||
AttachConsole(self.__parentPid)
|
||||
except Exception, ex:
|
||||
log_error(e)
|
||||
log_error(ex)
|
||||
self.__consin = None
|
||||
self.__consout = None
|
||||
raise e
|
||||
pass
|
||||
#log(e)
|
||||
#log(ex)
|
||||
return
|
||||
#self.__consin = None
|
||||
#self.__consout = None
|
||||
#raise e
|
||||
|
||||
self.__consin = GetStdHandle(STD_INPUT_HANDLE)
|
||||
self.__consout = self.getConsoleOut()
|
||||
|
||||
|
||||
|
||||
def switchBack(self):
|
||||
"""Releases from the current console and attaches
|
||||
@ -2042,7 +2109,7 @@ class Wtty:
|
||||
else:
|
||||
# Our original console has been free'd, allocate a new one
|
||||
AllocConsole()
|
||||
|
||||
|
||||
self.__consin = None
|
||||
self.__consout = None
|
||||
|
||||
@ -2087,12 +2154,18 @@ class Wtty:
|
||||
if s[-1] == '\n':
|
||||
s = s[:-1]
|
||||
records = [self.createKeyEvent(c) for c in unicode(s)]
|
||||
if not self.__consout:
|
||||
return ""
|
||||
consinfo = self.__consout.GetConsoleScreenBufferInfo()
|
||||
startCo = consinfo['CursorPosition']
|
||||
wrote = self.__consin.WriteConsoleInput(records)
|
||||
while self.__consin.PeekConsoleInput(8) != ():
|
||||
time.sleep(0)
|
||||
self.__consout.FillConsoleOutputCharacter(u'\0', len(s), startCo)
|
||||
ts = time.time()
|
||||
while self.__consin and self.__consin.PeekConsoleInput(8) != ():
|
||||
if time.time() > ts + len(s) * .05:
|
||||
break
|
||||
time.sleep(.05)
|
||||
if self.__consout:
|
||||
self.__consout.FillConsoleOutputCharacter(screenbufferfillchar, len(s), startCo)
|
||||
except:
|
||||
self.switchBack()
|
||||
raise
|
||||
@ -2116,7 +2189,7 @@ class Wtty:
|
||||
as a string."""
|
||||
|
||||
buff = []
|
||||
totalRead = 0
|
||||
self.lastRead = 0
|
||||
|
||||
startX = startCo.X
|
||||
startY = startCo.Y
|
||||
@ -2135,6 +2208,9 @@ class Wtty:
|
||||
endPoint = self.getPoint(endOff)
|
||||
|
||||
s = self.__consout.ReadConsoleOutputCharacter(readlen, startCo)
|
||||
ln = len(s)
|
||||
self.lastRead += ln
|
||||
self.totalRead += ln
|
||||
buff.append(s)
|
||||
|
||||
startX, startY = endPoint[0], endPoint[1]
|
||||
@ -2149,31 +2225,101 @@ class Wtty:
|
||||
characters or screen-buffer-fill characters."""
|
||||
|
||||
strlist = []
|
||||
for c in s:
|
||||
if ord(c) == 9834:
|
||||
strlist.append('\r')
|
||||
elif ord(c) == 9689:
|
||||
strlist.append('\n')
|
||||
elif ord(c) == 0:
|
||||
if strlist[-1:] != ['\r\n']:
|
||||
for i, c in enumerate(s):
|
||||
if c == screenbufferfillchar:
|
||||
if (self.totalRead - self.lastRead + i + 1) % 80 == 0:
|
||||
|
||||
|
||||
|
||||
|
||||
strlist.append('\r\n')
|
||||
else:
|
||||
strlist.append(c)
|
||||
|
||||
return ''.join(strlist).encode('ascii', 'ignore')
|
||||
s = ''.join(strlist)
|
||||
try:
|
||||
return s.encode(str(self.codepage), 'replace')
|
||||
except LookupError:
|
||||
return s.encode(getattr(sys.stdout, 'encoding', None) or
|
||||
sys.getdefaultencoding(), 'replace')
|
||||
|
||||
def readConsoleToCursor(self):
|
||||
"""Reads from the current read position to the current cursor
|
||||
position and inserts the string into self.__buffer."""
|
||||
|
||||
if not self.__consout:
|
||||
return ""
|
||||
|
||||
consinfo = self.__consout.GetConsoleScreenBufferInfo()
|
||||
cursorPos = consinfo['CursorPosition']
|
||||
|
||||
if cursorPos.X == self.__currentReadCo.X and cursorPos.Y == self.__currentReadCo.Y:
|
||||
return ''
|
||||
|
||||
s = self.readConsole(self.__currentReadCo, cursorPos)
|
||||
s = self.parseData(s)
|
||||
#log('=' * 80)
|
||||
#log('cursor: %r, current: %r' % (cursorPos, self.__currentReadCo))
|
||||
|
||||
isSameX = cursorPos.X == self.__currentReadCo.X
|
||||
isSameY = cursorPos.Y == self.__currentReadCo.Y
|
||||
isSamePos = isSameX and isSameY
|
||||
|
||||
#log('isSameY: %r' % isSameY)
|
||||
#log('isSamePos: %r' % isSamePos)
|
||||
|
||||
if isSameY or not self.lastReadData.endswith('\r\n'):
|
||||
# Read the current slice again
|
||||
self.totalRead -= self.lastRead
|
||||
self.__currentReadCo.X = 0
|
||||
self.__currentReadCo.Y = self.__bufferY
|
||||
|
||||
#log('cursor: %r, current: %r' % (cursorPos, self.__currentReadCo))
|
||||
|
||||
raw = self.readConsole(self.__currentReadCo, cursorPos)
|
||||
rawlist = []
|
||||
while raw:
|
||||
rawlist.append(raw[:self.__consSize[0]])
|
||||
raw = raw[self.__consSize[0]:]
|
||||
raw = ''.join(rawlist)
|
||||
s = self.parseData(raw)
|
||||
for i, line in enumerate(reversed(rawlist)):
|
||||
if line.endswith(screenbufferfillchar):
|
||||
# Record the Y offset where the most recent line break was detected
|
||||
self.__bufferY += len(rawlist) - i
|
||||
break
|
||||
|
||||
#log('lastReadData: %r' % self.lastReadData)
|
||||
#log('s: %r' % s)
|
||||
|
||||
#isSameData = False
|
||||
if isSamePos and self.lastReadData == s:
|
||||
#isSameData = True
|
||||
s = ''
|
||||
|
||||
#log('isSameData: %r' % isSameData)
|
||||
#log('s: %r' % s)
|
||||
|
||||
if s:
|
||||
lastReadData = self.lastReadData
|
||||
pos = self.getOffset(self.__currentReadCo.X, self.__currentReadCo.Y)
|
||||
self.lastReadData = s
|
||||
if isSameY or not lastReadData.endswith('\r\n'):
|
||||
# Detect changed lines
|
||||
self.__buffer.seek(pos)
|
||||
buf = self.__buffer.read()
|
||||
#log('buf: %r' % buf)
|
||||
#log('raw: %r' % raw)
|
||||
if raw.startswith(buf):
|
||||
# Line has grown
|
||||
rawslice = raw[len(buf):]
|
||||
# Update last read bytes so line breaks can be detected in parseData
|
||||
lastRead = self.lastRead
|
||||
self.lastRead = len(rawslice)
|
||||
s = self.parseData(rawslice)
|
||||
self.lastRead = lastRead
|
||||
else:
|
||||
# Cursor has been repositioned
|
||||
s = '\r' + s
|
||||
#log('s: %r' % s)
|
||||
self.__buffer.seek(pos)
|
||||
self.__buffer.truncate()
|
||||
self.__buffer.write(raw)
|
||||
|
||||
self.__currentReadCo.X = cursorPos.X
|
||||
self.__currentReadCo.Y = cursorPos.Y
|
||||
@ -2190,17 +2336,20 @@ class Wtty:
|
||||
try:
|
||||
while True:
|
||||
#Wait for child process to be paused
|
||||
if self.__currentReadCo.Y > 8000:
|
||||
if self.__currentReadCo.Y > maxconsoleY:
|
||||
time.sleep(.2)
|
||||
|
||||
start = time.clock()
|
||||
s = self.readConsoleToCursor()
|
||||
|
||||
if self.__currentReadCo.Y > maxconsoleY:
|
||||
self.refreshConsole()
|
||||
|
||||
if len(s) != 0:
|
||||
self.switchBack()
|
||||
return s
|
||||
|
||||
if timeout <= 0:
|
||||
if not self.isalive() or timeout <= 0:
|
||||
self.switchBack()
|
||||
return ''
|
||||
|
||||
@ -2208,11 +2357,13 @@ class Wtty:
|
||||
end = time.clock()
|
||||
timeout -= end - start
|
||||
|
||||
if self.__currentReadCo.Y > 8000:
|
||||
self.resetConsole()
|
||||
|
||||
|
||||
except Exception, e:
|
||||
log(e)
|
||||
log('End Of File (EOF) in Wtty.read_nonblocking().')
|
||||
self.switchBack()
|
||||
raise e
|
||||
raise EOF('End Of File (EOF) in Wtty.read_nonblocking().')
|
||||
|
||||
self.switchBack()
|
||||
return s
|
||||
@ -2226,7 +2377,15 @@ class Wtty:
|
||||
self.__currentReadCo.X = 0
|
||||
self.__currentReadCo.Y = 0
|
||||
writelen = self.__consSize[0] * self.__consSize[1]
|
||||
self.__consout.FillConsoleOutputCharacter(u'\4', writelen, orig)
|
||||
# Use NUL as fill char because it displays as whitespace
|
||||
# (if we interact() with the child)
|
||||
self.__consout.FillConsoleOutputCharacter(screenbufferfillchar, writelen, orig)
|
||||
|
||||
self.__bufferY = 0
|
||||
self.__buffer.truncate(0)
|
||||
#consinfo = self.__consout.GetConsoleScreenBufferInfo()
|
||||
#cursorPos = consinfo['CursorPosition']
|
||||
#log('refreshConsole: cursorPos %s' % cursorPos)
|
||||
|
||||
|
||||
def setecho(self, state):
|
||||
@ -2329,7 +2488,21 @@ class Wtty:
|
||||
|
||||
class ConsoleReader:
|
||||
|
||||
def __init__(self, path, pid, tid, env = None):
|
||||
def __init__(self, path, pid, tid, env = None, cp=None, logdir=None):
|
||||
self.logdir = logdir
|
||||
log('=' * 80, 'consolereader', logdir)
|
||||
log("OEM code page: %s" % windll.kernel32.GetOEMCP(), 'consolereader', logdir)
|
||||
log("ANSI code page: %s" % windll.kernel32.GetACP(), 'consolereader', logdir)
|
||||
log("Console output code page: %s" % windll.kernel32.GetConsoleOutputCP(), 'consolereader', logdir)
|
||||
if cp:
|
||||
log("Setting console output code page to %s" % cp, 'consolereader', logdir)
|
||||
try:
|
||||
SetConsoleOutputCP(cp)
|
||||
except Exception, e:
|
||||
log(e, 'consolereader', logdir)
|
||||
else:
|
||||
log("Console output code page: %s" % windll.kernel32.GetConsoleOutputCP(), 'consolereader', logdir)
|
||||
log('Spawning %s' % path, 'consolereader', logdir)
|
||||
try:
|
||||
try:
|
||||
consout = self.getConsoleOut()
|
||||
@ -2339,7 +2512,7 @@ class ConsoleReader:
|
||||
self.__childProcess, _, childPid, self.__tid = CreateProcess(None, path, None, None, False,
|
||||
0, None, None, si)
|
||||
except Exception, e:
|
||||
log_error(e)
|
||||
log(e, 'consolereader', logdir)
|
||||
time.sleep(.1)
|
||||
win32api.PostThreadMessage(int(tid), WM_USER, 0, 0)
|
||||
sys.exit()
|
||||
@ -2356,6 +2529,7 @@ class ConsoleReader:
|
||||
cursorPos = consinfo['CursorPosition']
|
||||
|
||||
if GetExitCodeProcess(parent) != STILL_ACTIVE or GetExitCodeProcess(self.__childProcess) != STILL_ACTIVE:
|
||||
time.sleep(.1)
|
||||
try:
|
||||
TerminateProcess(self.__childProcess, 0)
|
||||
except pywintypes.error, e:
|
||||
@ -2364,22 +2538,30 @@ class ConsoleReader:
|
||||
# Don't log. Child process will exit regardless when
|
||||
# calling sys.exit
|
||||
if e.args[0] != winerror.ERROR_ACCESS_DENIED:
|
||||
log_error(e)
|
||||
log(e, 'consolereader', logdir)
|
||||
sys.exit()
|
||||
|
||||
if cursorPos.Y > 8000:
|
||||
if cursorPos.Y > maxconsoleY and not paused:
|
||||
#log('ConsoleReader.__init__: cursorPos %s'
|
||||
#% cursorPos, 'consolereader', logdir)
|
||||
#log('suspendThread', 'consolereader', logdir)
|
||||
self.suspendThread()
|
||||
paused = True
|
||||
|
||||
if cursorPos.Y <= 8000 and paused:
|
||||
if cursorPos.Y <= maxconsoleY and paused:
|
||||
#log('ConsoleReader.__init__: cursorPos %s'
|
||||
#% cursorPos, 'consolereader', logdir)
|
||||
#log('resumeThread', 'consolereader', logdir)
|
||||
self.resumeThread()
|
||||
paused = False
|
||||
|
||||
time.sleep(.1)
|
||||
except Exception, e:
|
||||
log_error(e)
|
||||
log(e, 'consolereader', logdir)
|
||||
time.sleep(.1)
|
||||
|
||||
def handler(self, sig):
|
||||
log_error(sig)
|
||||
log(sig, 'consolereader', logdir)
|
||||
return False
|
||||
|
||||
def getConsoleOut(self):
|
||||
@ -2393,13 +2575,15 @@ class ConsoleReader:
|
||||
|
||||
return PyConsoleScreenBufferType(consout)
|
||||
|
||||
def initConsole(self, consout):
|
||||
rect = PySMALL_RECTType(0, 0, 79, 70)
|
||||
def initConsole(self, consout):
|
||||
rect = PySMALL_RECTType(0, 0, 79, 24)
|
||||
consout.SetConsoleWindowInfo(True, rect)
|
||||
size = PyCOORDType(80, 16000)
|
||||
consout.SetConsoleScreenBufferSize(size)
|
||||
pos = PyCOORDType(0, 0)
|
||||
consout.FillConsoleOutputCharacter(u'\0', size.X * size.Y, pos)
|
||||
# Use NUL as fill char because it displays as whitespace
|
||||
# (if we interact() with the child)
|
||||
consout.FillConsoleOutputCharacter(screenbufferfillchar, size.X * size.Y, pos)
|
||||
|
||||
def suspendThread(self):
|
||||
"""Pauses the main thread of the child process."""
|
||||
@ -2595,27 +2779,76 @@ class searcher_re (object):
|
||||
self.end = self.match.end()
|
||||
return best_index
|
||||
|
||||
def log_error(e):
|
||||
def log(e, suffix='', logdir=None):
|
||||
if isinstance(e, Exception):
|
||||
# Get the full traceback
|
||||
e = traceback.format_exc()
|
||||
if hasattr(sys.stdout, 'isatty') and sys.stdout.isatty():
|
||||
# Only try to print if stdout is a tty, otherwise we might get
|
||||
# an 'invalid handle' exception
|
||||
print e
|
||||
# Log to the script (or packed executable if running 'frozen') directory
|
||||
# if it is writable (packed executable might be installed to a location
|
||||
# where we don't have write access)
|
||||
dirname = os.path.dirname(sys.executable if getattr(sys, 'frozen', False) else os.path.abspath(__file__))
|
||||
if os.access(dirname, os.W_OK):
|
||||
fout = open(os.path.join(dirname, 'pexpect_error.txt'), 'a')
|
||||
fout.write(str(e) + '\n')
|
||||
fout.close()
|
||||
#if hasattr(sys.stdout, 'isatty') and sys.stdout.isatty():
|
||||
## Only try to print if stdout is a tty, otherwise we might get
|
||||
## an 'invalid handle' exception
|
||||
#print e
|
||||
if not logdir:
|
||||
if getattr(sys, 'frozen', False):
|
||||
logdir = os.path.splitext(os.path.basename(sys.executable))[0]
|
||||
else:
|
||||
logdir = os.path.split(os.path.dirname(os.path.abspath(__file__)))
|
||||
if logdir[-1] == 'lib':
|
||||
logdir.pop()
|
||||
logdir = logdir[-1]
|
||||
if sys.platform == "win32":
|
||||
logdir = os.path.join(SHGetSpecialFolderPath(0, CSIDL_APPDATA),
|
||||
logdir, "logs")
|
||||
elif sys.platform == "darwin":
|
||||
logdir = os.path.join(os.path.expanduser("~"), "Library", "Logs",
|
||||
logdir)
|
||||
else:
|
||||
logdir = os.path.join(os.getenv("XDG_DATA_HOME",
|
||||
os.path.expandvars("$HOME/.local/share")),
|
||||
logdir, "logs")
|
||||
if not os.path.exists(logdir):
|
||||
try:
|
||||
os.makedirs(logdir)
|
||||
except (OSError, WindowsError):
|
||||
pass
|
||||
if os.path.isdir(logdir) and os.access(logdir, os.W_OK):
|
||||
logfile = os.path.join(logdir, 'wexpect%s.log' % suffix)
|
||||
if os.path.isfile(logfile):
|
||||
try:
|
||||
logstat = os.stat(logfile)
|
||||
except Exception, exception:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
mtime = time.localtime(logstat.st_mtime)
|
||||
except ValueError, exception:
|
||||
# This can happen on Windows because localtime() is buggy on
|
||||
# that platform. See:
|
||||
# http://stackoverflow.com/questions/4434629/zipfile-module-in-python-runtime-problems
|
||||
# http://bugs.python.org/issue1760357
|
||||
# To overcome this problem, we ignore the real modification
|
||||
# date and force a rollover
|
||||
mtime = time.localtime(time() - 60 * 60 * 24)
|
||||
if time.localtime()[:3] > mtime[:3]:
|
||||
# do rollover
|
||||
try:
|
||||
os.remove(logfile)
|
||||
except Exception, exception:
|
||||
pass
|
||||
try:
|
||||
fout = open(logfile, 'a')
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
ts = time.time()
|
||||
fout.write('%s,%s %s\n' % (time.strftime("%Y-%m-%d %H:%M:%S",
|
||||
time.localtime(ts)),
|
||||
('%3f' % (ts - int(ts)))[2:5], e))
|
||||
fout.close()
|
||||
|
||||
def excepthook(etype, value, tb):
|
||||
log_error(''.join(traceback.format_exception(etype, value, tb)))
|
||||
log(''.join(traceback.format_exception(etype, value, tb)))
|
||||
|
||||
sys.excepthook = excepthook
|
||||
#sys.excepthook = excepthook
|
||||
|
||||
def which (filename):
|
||||
|
||||
@ -2643,13 +2876,13 @@ def which (filename):
|
||||
if os.access(f, os.X_OK):
|
||||
return f
|
||||
return None
|
||||
|
||||
|
||||
def join_args(args):
|
||||
"""Joins arguments into a command line. It quotes all arguments that contain
|
||||
spaces or any of the characters ^!$%&()[]"""
|
||||
spaces or any of the characters ^!$%&()[]{}=;'+,`~"""
|
||||
commandline = []
|
||||
for arg in args:
|
||||
if re.search('[\^!$%&()[\]\s]', arg):
|
||||
if re.search('[\^!$%&()[\]{}=;\'+,`~\s]', arg):
|
||||
arg = '"%s"' % arg
|
||||
commandline.append(arg)
|
||||
return ' '.join(commandline)
|
||||
@ -2707,4 +2940,4 @@ def split_command_line(command_line):
|
||||
|
||||
if arg != '':
|
||||
arg_list.append(arg)
|
||||
return arg_list
|
||||
return arg_list
|
||||
|
Loading…
Reference in New Issue
Block a user