mirror of
https://github.com/clearml/wexpect-venv
synced 2025-06-26 18:15:52 +00:00
Fix blocking pipe connection
The console application could be run forever in case of connection errors
This commit is contained in:
parent
290ceebc2d
commit
c8977d8460
@ -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.
|
||||||
|
|
||||||
|
@ -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")
|
||||||
win32pipe.ConnectNamedPipe(self.pipe, None)
|
while True:
|
||||||
|
if end_time < time.time():
|
||||||
|
raise TIMEOUT('Connect to child has been timed out.')
|
||||||
|
try:
|
||||||
|
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):
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user