Fix blocking pipe connection

The console application could be run forever in case of connection errors
This commit is contained in:
Benedek Racz 2020-04-18 12:45:51 +02:00
parent 290ceebc2d
commit c8977d8460
3 changed files with 34 additions and 7 deletions

View File

@ -11,7 +11,7 @@ This project fixes these limitations, with example codes, tests, and pypi integr
Refactor Refactor
^^^^^^^^ ^^^^^^^^
The original wexpect was a monolite, one-file code, with several structural weaknesses. This leads The original wexpect was a monolith, one-file code, with several structural weaknesses. This leads
me to rewrite the whole code. The first variant of the new structure is delivered with me to rewrite the whole code. The first variant of the new structure is delivered with
`v3.2.0 <https://pypi.org/project/wexpect/3.2.0/>`_. (The default is the old variant `v3.2.0 <https://pypi.org/project/wexpect/3.2.0/>`_. (The default is the old variant
(:code:`legacy_wexpect`) in v3.2.0. :code:`WEXPECT_SPAWN_CLASS` environment variable can choose the (:code:`legacy_wexpect`) in v3.2.0. :code:`WEXPECT_SPAWN_CLASS` environment variable can choose the
@ -26,7 +26,7 @@ Generally, wexpect (both old and new) has three processes:
- *host* is our original python script/program, which want to launch the child. - *host* is our original python script/program, which want to launch the child.
- *console* is a process which started by the host, and launches the child. (This is a python script) - *console* is a process which started by the host, and launches the child. (This is a python script)
- *child* is the process which want to be launced. - *child* is the process which want to be launched.
The child and the console has a common Windows console, distict from the host. The child and the console has a common Windows console, distict from the host.

View File

@ -27,6 +27,7 @@ import socket
from .wexpect_util import init_logger from .wexpect_util import init_logger
from .wexpect_util import EOF_CHAR from .wexpect_util import EOF_CHAR
from .wexpect_util import SIGNAL_CHARS from .wexpect_util import SIGNAL_CHARS
from .wexpect_util import TIMEOUT
# #
# System-wide constants # System-wide constants
@ -74,6 +75,8 @@ class ConsoleReaderBase:
self.child_process = None self.child_process = None
self.child_pid = None self.child_pid = None
self.enable_signal_chars = True self.enable_signal_chars = True
self.timeout = 30
self.child_exitstatus = None
logger.info(f'ConsoleReader started. location {os.path.abspath(__file__)}') logger.info(f'ConsoleReader started. location {os.path.abspath(__file__)}')
@ -504,17 +507,32 @@ class ConsoleReaderSocket(ConsoleReaderBase):
class ConsoleReaderPipe(ConsoleReaderBase): class ConsoleReaderPipe(ConsoleReaderBase):
def create_connection(self, **kwargs): def create_connection(self, timeout=-1, **kwargs):
if timeout == -1:
timeout = self.timeout
if timeout is None:
end_time = float('inf')
else:
end_time = time.time() + timeout
pipe_name = 'wexpect_{}'.format(self.console_pid) pipe_name = 'wexpect_{}'.format(self.console_pid)
pipe_full_path = r'\\.\pipe\{}'.format(pipe_name) pipe_full_path = r'\\.\pipe\{}'.format(pipe_name)
logger.info('Start pipe server: %s', pipe_full_path) logger.info('Start pipe server: %s', pipe_full_path)
self.pipe = win32pipe.CreateNamedPipe( self.pipe = win32pipe.CreateNamedPipe(
pipe_full_path, pipe_full_path,
win32pipe.PIPE_ACCESS_DUPLEX, win32pipe.PIPE_ACCESS_DUPLEX,
win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_READMODE_MESSAGE | win32pipe.PIPE_WAIT, win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_READMODE_MESSAGE | win32pipe.PIPE_NOWAIT,
1, 65536, 65536, 0, None) 1, 65536, 65536, 10000, None)
logger.info("waiting for client") logger.info("waiting for client")
while True:
if end_time < time.time():
raise TIMEOUT('Connect to child has been timed out.')
try:
win32pipe.ConnectNamedPipe(self.pipe, None) win32pipe.ConnectNamedPipe(self.pipe, None)
break
except Exception as e:
logger.debug(e)
time.sleep(0.2)
logger.info('got client') logger.info('got client')
def close_connection(self): def close_connection(self):

View File

@ -881,11 +881,20 @@ class SpawnPipe(SpawnBase):
# seconds. # seconds.
self.delayafterterminate = 2 self.delayafterterminate = 2
def connect_to_child(self): def connect_to_child(self, timeout=-1):
if timeout == -1:
timeout = self.timeout
if timeout is None:
end_time = float('inf')
else:
end_time = time.time() + timeout
pipe_name = 'wexpect_{}'.format(self.console_pid) pipe_name = 'wexpect_{}'.format(self.console_pid)
pipe_full_path = r'\\.\pipe\{}'.format(pipe_name) pipe_full_path = r'\\.\pipe\{}'.format(pipe_name)
logger.debug(f'Trying to connect to pipe: {pipe_full_path}') logger.debug(f'Trying to connect to pipe: {pipe_full_path}')
while True: while True:
if end_time < time.time():
raise TIMEOUT('Connect to child has been timed out.')
try: try:
self.pipe = win32file.CreateFile( self.pipe = win32file.CreateFile(
pipe_full_path, pipe_full_path,