py_func an lua_func processes processes faster.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
import time
|
||||||
import pprint
|
import pprint
|
||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ class TestItemPyFunc(TestItem):
|
|||||||
f"The '{self.cmd()}' test item named '{self.name()}' (child of '{self.parent.name()}') has a missing or wrong parameter",
|
f"The '{self.cmd()}' test item named '{self.name()}' (child of '{self.parent.name()}') has a missing or wrong parameter",
|
||||||
self.seqFilename(),
|
self.seqFilename(),
|
||||||
)
|
)
|
||||||
self._proc = PyFuncExecEngine(tm.gd("python_bin", ""), api_request, 10)
|
self._py_func_proc = PyFuncExecEngine(tm.gd("python_bin", ""), api_request, 10)
|
||||||
|
|
||||||
@test_run
|
@test_run
|
||||||
def execute(self):
|
def execute(self):
|
||||||
@@ -48,21 +48,26 @@ class TestItemPyFunc(TestItem):
|
|||||||
tm.print_debug("Parameters list:")
|
tm.print_debug("Parameters list:")
|
||||||
tm.print_debug(textwrap.indent(pprint.pformat(pl), " |"))
|
tm.print_debug(textwrap.indent(pprint.pformat(pl), " |"))
|
||||||
|
|
||||||
# start the process for executing external python
|
# start the process for executing external python
|
||||||
self._proc.start()
|
t0 = time.monotonic()
|
||||||
if not self._proc.wait_ready(10):
|
self._py_func_proc.start()
|
||||||
raise ETUMRuntimeError(
|
t1 = time.monotonic()
|
||||||
f"""Impossible to start the external python execution process.
|
if not self._py_func_proc.wait_ready():
|
||||||
|
raise ETUMRuntimeError(
|
||||||
|
f"""Impossible to start the external python execution process.
|
||||||
Is the python path correct ?
|
Is the python path correct ?
|
||||||
python_bin = {tm.gd("python_bin", "no python path defined")}"""
|
python_bin = {tm.gd("python_bin", "no python path defined")}"""
|
||||||
)
|
)
|
||||||
|
t2 = time.monotonic()
|
||||||
|
tm.print_info(f"t1 = {(t1-t0):0.2f}. t2 = {(t2-t1):0.2f}")
|
||||||
try:
|
try:
|
||||||
success, ret = self._proc.func_call(self.file_name, self.func_name, pl)
|
success, ret = self._py_func_proc.func_call(self.file_name, self.func_name, pl)
|
||||||
finally:
|
finally:
|
||||||
# Stops python function execution process
|
# Stops python function execution process
|
||||||
self._proc.stop()
|
self._py_func_proc.stop()
|
||||||
self._proc.join()
|
t3 = time.monotonic()
|
||||||
|
self._py_func_proc.join()
|
||||||
|
tm.print_info(f"t3 = {(t3-t2):0.3f}. t4 = {(time.monotonic()-t3):0.3f}")
|
||||||
|
|
||||||
if success == TestValue.SUCCESS:
|
if success == TestValue.SUCCESS:
|
||||||
self.result.set(TestValue.SUCCESS)
|
self.result.set(TestValue.SUCCESS)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import socket
|
import socket
|
||||||
import json
|
import json
|
||||||
|
from time import monotonic
|
||||||
import threading
|
import threading
|
||||||
import itertools
|
import itertools
|
||||||
from time import sleep
|
from time import sleep
|
||||||
@@ -58,7 +59,6 @@ class JsonRpcConnection:
|
|||||||
name,
|
name,
|
||||||
conn: socket.socket,
|
conn: socket.socket,
|
||||||
req_handler: Callable[..., Any],
|
req_handler: Callable[..., Any],
|
||||||
timeout=0.2,
|
|
||||||
dbg_out=None,
|
dbg_out=None,
|
||||||
):
|
):
|
||||||
self.name = name
|
self.name = name
|
||||||
@@ -74,8 +74,9 @@ class JsonRpcConnection:
|
|||||||
self.id_gen = itertools.count(1)
|
self.id_gen = itertools.count(1)
|
||||||
self.running = True
|
self.running = True
|
||||||
self._dbg_out = dbg_out
|
self._dbg_out = dbg_out
|
||||||
|
self._event_ready = threading.Event()
|
||||||
|
|
||||||
self.conn.settimeout(timeout)
|
self.conn.settimeout(0.1)
|
||||||
|
|
||||||
self.recv_thread = threading.Thread(target=self._recv_loop, daemon=True)
|
self.recv_thread = threading.Thread(target=self._recv_loop, daemon=True)
|
||||||
self.recv_thread.start()
|
self.recv_thread.start()
|
||||||
@@ -93,6 +94,7 @@ class JsonRpcConnection:
|
|||||||
buffer = b""
|
buffer = b""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
self._event_ready.set()
|
||||||
while self.running:
|
while self.running:
|
||||||
try:
|
try:
|
||||||
data = self.conn.recv(4096)
|
data = self.conn.recv(4096)
|
||||||
@@ -222,6 +224,12 @@ class JsonRpcConnection:
|
|||||||
def join(self):
|
def join(self):
|
||||||
self.recv_thread.join()
|
self.recv_thread.join()
|
||||||
|
|
||||||
|
def is_alive(self):
|
||||||
|
self.recv_thread.is_alive()
|
||||||
|
|
||||||
|
def wait_ready(self, timeout=None):
|
||||||
|
return self._event_ready.wait(timeout)
|
||||||
|
|
||||||
|
|
||||||
class JsonRpcBase(threading.Thread):
|
class JsonRpcBase(threading.Thread):
|
||||||
"""Threaded base class for simple JSON-RPC server/client helpers.
|
"""Threaded base class for simple JSON-RPC server/client helpers.
|
||||||
@@ -292,6 +300,7 @@ class JsonRpcBase(threading.Thread):
|
|||||||
self._rpc = JsonRpcConnection(
|
self._rpc = JsonRpcConnection(
|
||||||
self.name, sock, self.handle_request, dbg_out=self.dbg_out
|
self.name, sock, self.handle_request, dbg_out=self.dbg_out
|
||||||
)
|
)
|
||||||
|
self._rpc.wait_ready()
|
||||||
self._event_ready.set()
|
self._event_ready.set()
|
||||||
|
|
||||||
def wait_ready(self, timeout=None):
|
def wait_ready(self, timeout=None):
|
||||||
@@ -342,16 +351,12 @@ class JsonRpcSrv(JsonRpcBase):
|
|||||||
self.print_info(f"listening on {self._host}:{self._port}")
|
self.print_info(f"listening on {self._host}:{self._port}")
|
||||||
|
|
||||||
self.print_info(f"awaiting connection for {self._timeout} secs")
|
self.print_info(f"awaiting connection for {self._timeout} secs")
|
||||||
tslice = 0.2
|
sock.settimeout(self._timeout)
|
||||||
sock.settimeout(tslice)
|
|
||||||
t = self._timeout
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
conn, addr = sock.accept()
|
conn, addr = sock.accept()
|
||||||
break
|
break
|
||||||
except socket.timeout:
|
except socket.timeout:
|
||||||
t -= tslice
|
|
||||||
if t < 0:
|
|
||||||
raise ETUMRuntimeError(f"{self.name}: Timeout")
|
raise ETUMRuntimeError(f"{self.name}: Timeout")
|
||||||
|
|
||||||
self.print_info("Client connected")
|
self.print_info("Client connected")
|
||||||
@@ -439,22 +444,20 @@ class JsonRpcClient(JsonRpcBase):
|
|||||||
# TCP/IP socket creation
|
# TCP/IP socket creation
|
||||||
try:
|
try:
|
||||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||||
tslice = 0.5
|
tslice = 0.1
|
||||||
t = self._timeout
|
t0 = monotonic()
|
||||||
sock.settimeout(tslice)
|
sock.settimeout(0)
|
||||||
# Link of the socket at the configured port
|
# Link of the socket at the configured port
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
sleep(tslice)
|
||||||
sock.connect((self._host, self._port))
|
sock.connect((self._host, self._port))
|
||||||
break
|
break
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
t -= tslice
|
if (monotonic() - t0) > self._timeout:
|
||||||
if t < 0:
|
|
||||||
raise ETUMRuntimeError(
|
raise ETUMRuntimeError(
|
||||||
f"{self.name}: failed to connect : {e}"
|
f"{self.name}: failed to connect : {e}"
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
sleep(tslice)
|
|
||||||
|
|
||||||
self.print_info("Connected to server")
|
self.print_info("Connected to server")
|
||||||
self.connect(sock)
|
self.connect(sock)
|
||||||
|
|||||||
@@ -84,25 +84,28 @@ class PyProcessBase:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, python_bin="", request_handler=None, timeout=10, python_path=""):
|
def __init__(self, python_bin="", request_handler=None, timeout=10, python_path=""):
|
||||||
if (python_bin is not None) and (python_bin != ""):
|
self._pbin = python_bin
|
||||||
|
if (self._pbin is not None) and (self._pbin != ""):
|
||||||
|
|
||||||
if shutil.which(python_bin) is None:
|
if shutil.which(self._pbin) is None:
|
||||||
raise ETUMRuntimeError(
|
raise ETUMRuntimeError(
|
||||||
f"The passed python path is not pointing to an executable: '{python_bin}'"
|
f"The passed python path is not pointing to an executable: '{self._pbin}'"
|
||||||
)
|
)
|
||||||
|
|
||||||
if not _is_python_interpreter(python_bin):
|
if not _is_python_interpreter(self._pbin):
|
||||||
raise ETUMRuntimeError(
|
raise ETUMRuntimeError(
|
||||||
f"The passed executable is not a python interpreter: '{python_bin}'"
|
f"The passed executable is not a python interpreter: '{self._pbin}'"
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
python_bin = _sys_python_bin()
|
self._pbin = tm.gd("_cached_python_bin", "")
|
||||||
if python_bin == "":
|
if self._pbin == "":
|
||||||
raise ETUMRuntimeError(f"No valid python interpreter found")
|
self._pbin = _sys_python_bin()
|
||||||
tm.setgd("python_bin", python_bin)
|
tm.setgd("_cached_python_bin", self._pbin)
|
||||||
|
|
||||||
|
if self._pbin == "":
|
||||||
|
raise ETUMRuntimeError(f"No valid python interpreter found")
|
||||||
|
|
||||||
self._pbin = python_bin
|
|
||||||
self._ppath = python_path
|
self._ppath = python_path
|
||||||
self._req_handler = request_handler
|
self._req_handler = request_handler
|
||||||
self._process = None
|
self._process = None
|
||||||
@@ -110,6 +113,7 @@ class PyProcessBase:
|
|||||||
self._timeout = timeout
|
self._timeout = timeout
|
||||||
self._rpc = None
|
self._rpc = None
|
||||||
|
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""
|
"""
|
||||||
run the subprocess to execute the python functions of the test.
|
run the subprocess to execute the python functions of the test.
|
||||||
@@ -132,6 +136,9 @@ class PyProcessBase:
|
|||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
sock.bind(("localhost", 0))
|
sock.bind(("localhost", 0))
|
||||||
self._port = sock.getsockname()[1]
|
self._port = sock.getsockname()[1]
|
||||||
|
# Port was reserved until the sub-process is started. Now released.
|
||||||
|
if sock is not None:
|
||||||
|
sock.close()
|
||||||
|
|
||||||
# Add the path of the subprocess (root sources of testium)
|
# Add the path of the subprocess (root sources of testium)
|
||||||
func_proc_path = testium_path()
|
func_proc_path = testium_path()
|
||||||
@@ -152,10 +159,6 @@ class PyProcessBase:
|
|||||||
|
|
||||||
self._process = subprocess.Popen(params, env=env, cwd=func_proc_path)
|
self._process = subprocess.Popen(params, env=env, cwd=func_proc_path)
|
||||||
|
|
||||||
# Port was reserved until the sub-process is started. Now released.
|
|
||||||
if sock is not None:
|
|
||||||
sock.close()
|
|
||||||
|
|
||||||
self._rpc = JsonRpcClient(
|
self._rpc = JsonRpcClient(
|
||||||
"localhost", self._port, req_handler=self._req_handler
|
"localhost", self._port, req_handler=self._req_handler
|
||||||
)
|
)
|
||||||
@@ -163,6 +166,11 @@ class PyProcessBase:
|
|||||||
self._rpc.dbg_out = sys.stdout
|
self._rpc.dbg_out = sys.stdout
|
||||||
self._rpc.start()
|
self._rpc.start()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def python_bin(self):
|
||||||
|
return self._pbin
|
||||||
|
|
||||||
|
|
||||||
def join(self):
|
def join(self):
|
||||||
if self._rpc is not None:
|
if self._rpc is not None:
|
||||||
self._rpc.join()
|
self._rpc.join()
|
||||||
|
|||||||
Reference in New Issue
Block a user