esp-test-utils 0.3.3__tar.gz → 0.3.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/CHANGELOG.md +9 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/PKG-INFO +1 -1
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esp_test_utils.egg-info/PKG-INFO +1 -1
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esp_test_utils.egg-info/SOURCES.txt +6 -1
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/adapter/dut/dut_base.py +43 -2
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/adapter/dut/esp_dut.py +7 -3
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/adapter/port/base_port.py +122 -41
- esp_test_utils-0.3.4/esptest/adapter/port/data_monitor_mixin.py +36 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/adapter/port/serial_port.py +2 -2
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/adapter/port/shell_port.py +3 -1
- esp_test_utils-0.3.4/esptest/common/data_monitor.py +134 -0
- esp_test_utils-0.3.4/esptest/config/global_config.py +18 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/interface/port.py +14 -0
- esp_test_utils-0.3.4/example/use_data_monitor.py +56 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/adapter/test_Dut.py +210 -2
- esp_test_utils-0.3.4/tests/adapter/test_base_port.py +160 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/adapter/test_shell_port.py +59 -3
- esp_test_utils-0.3.4/tests/basic/test_data_monitor.py +174 -0
- esp_test_utils-0.3.4/tests/test_global_config.py +47 -0
- esp_test_utils-0.3.3/esptest/common/data_monitor.py +0 -52
- esp_test_utils-0.3.3/esptest/config/default_config.py +0 -2
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/.github/.gitkeep +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/.github/workflows/pypi-publish.yml +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/.gitignore +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/.gitlab-ci.yml +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/.pre-commit-config.yaml +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/CONTRIBUTING.md +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/LICENSE +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/README.md +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/docs/Makefile +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/docs/conf.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/docs/index.rst +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/docs/make.bat +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esp_test_utils.egg-info/dependency_links.txt +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esp_test_utils.egg-info/entry_points.txt +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esp_test_utils.egg-info/requires.txt +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esp_test_utils.egg-info/top_level.txt +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/__init__.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/__main__.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/adapter/__init__.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/adapter/dut/__init__.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/adapter/dut/create_dut.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/adapter/dut/esp_mixin.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/adapter/dut/esp_port.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/adapter/dut/mac_mixin.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/adapter/dut/wrapper.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/adapter/port/__init__.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/all.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/common/__init__.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/common/compat_typing.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/common/decorators.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/common/encoding.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/common/generator.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/common/parser.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/common/shell.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/common/timestamp.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/config/__init__.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/config/env_config.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/db/__init__.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/db/runners.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/devices/__init__.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/devices/attenuator.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/devices/esp_serial.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/devices/serial_dut.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/devices/serial_tools.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/devices/switch.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/env/__init__.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/env/base_env.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/env/wifi_env.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/esp_console/__init__.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/esp_console/wifi_cmd.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/interface/__init__.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/interface/dut.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/iperf_utility/__init__.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/iperf_utility/iperf_results.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/iperf_utility/iperf_test.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/iperf_utility/iperf_test.test.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/iperf_utility/line_chart.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/logger/__init__.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/logger/logger.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/network/__init__.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/network/mac.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/network/netif.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/network/nic.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/scripts/downbin.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/scripts/list_ports.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/scripts/monitor.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/scripts/set_att.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/testcase/__init__.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/testcase/result.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/tools/copy_bin.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/tools/download_bin.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/tools/http_download.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/tools/pip_check.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/tools/uart_monitor.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/utility/gen_esp32part.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/esptest/utility/parse_bin_path.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/example/jap_test.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/example/restart_test.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/pyproject.toml +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/setup.cfg +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/__init__.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/basic/test_decorators.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/basic/test_network.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/conftest.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/db/test_db_runners.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/devices/test_switch.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/esp_console/_files/wifi_cmd_connected_1.log +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/esp_console/_files/wifi_cmd_connected_2.log +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/esp_console/conftest.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/esp_console/test_WifiCmd.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/iperf_utility/_files/dut_iperf_rx1.log +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/iperf_utility/_files/dut_iperf_rx2.log +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/iperf_utility/_files/pc_iperf_rx.log +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/iperf_utility/_files/pc_iperf_rx2.log +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/iperf_utility/test_chart.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/iperf_utility/test_iperf_results.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/iperf_utility/test_iperf_util.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/test_EnvConfig.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/test_common.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/test_import.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/test_parse_expand_list.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/testcase/test_result.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/tools/test_download_bin.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/tools/test_download_file.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/tools/test_pip_check.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/utility/_files/test-bin.zip +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/utility/_files/test-get-baud/ESP32AT-V4.1.1.0/sdkconfig +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tests/utility/test_parse_bin_path.py +0 -0
- {esp_test_utils-0.3.3 → esp_test_utils-0.3.4}/tools/ci/check_dev_version.py +0 -0
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## v0.3.4 (2026-04-27)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
- fix: make serial read error reconnect configurable
|
|
5
|
+
- fix: resolve data monitor review findings
|
|
6
|
+
- feat: support add data monitor and callback to port
|
|
7
|
+
- fix(port): propagate monitors and callbacks consistently
|
|
8
|
+
- fix: pexpect buffer maxread limit and data cache overflow
|
|
9
|
+
|
|
1
10
|
## v0.3.3 (2026-04-21)
|
|
2
11
|
|
|
3
12
|
|
|
@@ -32,6 +32,7 @@ esptest/adapter/dut/mac_mixin.py
|
|
|
32
32
|
esptest/adapter/dut/wrapper.py
|
|
33
33
|
esptest/adapter/port/__init__.py
|
|
34
34
|
esptest/adapter/port/base_port.py
|
|
35
|
+
esptest/adapter/port/data_monitor_mixin.py
|
|
35
36
|
esptest/adapter/port/serial_port.py
|
|
36
37
|
esptest/adapter/port/shell_port.py
|
|
37
38
|
esptest/common/__init__.py
|
|
@@ -44,8 +45,8 @@ esptest/common/parser.py
|
|
|
44
45
|
esptest/common/shell.py
|
|
45
46
|
esptest/common/timestamp.py
|
|
46
47
|
esptest/config/__init__.py
|
|
47
|
-
esptest/config/default_config.py
|
|
48
48
|
esptest/config/env_config.py
|
|
49
|
+
esptest/config/global_config.py
|
|
49
50
|
esptest/db/__init__.py
|
|
50
51
|
esptest/db/runners.py
|
|
51
52
|
esptest/devices/__init__.py
|
|
@@ -88,14 +89,18 @@ esptest/utility/gen_esp32part.py
|
|
|
88
89
|
esptest/utility/parse_bin_path.py
|
|
89
90
|
example/jap_test.py
|
|
90
91
|
example/restart_test.py
|
|
92
|
+
example/use_data_monitor.py
|
|
91
93
|
tests/__init__.py
|
|
92
94
|
tests/conftest.py
|
|
93
95
|
tests/test_EnvConfig.py
|
|
94
96
|
tests/test_common.py
|
|
97
|
+
tests/test_global_config.py
|
|
95
98
|
tests/test_import.py
|
|
96
99
|
tests/test_parse_expand_list.py
|
|
97
100
|
tests/adapter/test_Dut.py
|
|
101
|
+
tests/adapter/test_base_port.py
|
|
98
102
|
tests/adapter/test_shell_port.py
|
|
103
|
+
tests/basic/test_data_monitor.py
|
|
99
104
|
tests/basic/test_decorators.py
|
|
100
105
|
tests/basic/test_network.py
|
|
101
106
|
tests/db/test_db_runners.py
|
|
@@ -11,12 +11,14 @@ from esptool.loader import ESPLoader
|
|
|
11
11
|
|
|
12
12
|
import esptest.common.compat_typing as t
|
|
13
13
|
|
|
14
|
+
from ...common.data_monitor import DataMonitor
|
|
14
15
|
from ...common.timestamp import timestamp_slug
|
|
15
16
|
from ...interface.dut import DutInterface
|
|
16
17
|
from ...interface.port import PortInterface
|
|
17
18
|
from ...logger import get_logger
|
|
18
19
|
from ...utility.parse_bin_path import ParseBinPath, get_baud_from_bin_path
|
|
19
20
|
from ..port.base_port import BasePort, RawPort
|
|
21
|
+
from ..port.data_monitor_mixin import DataMonitorMixin
|
|
20
22
|
from ..port.serial_port import SerialPort
|
|
21
23
|
|
|
22
24
|
logger = get_logger('dut')
|
|
@@ -131,16 +133,30 @@ class VariablesMixin:
|
|
|
131
133
|
self._dynamic_variables.pop(name)
|
|
132
134
|
|
|
133
135
|
|
|
134
|
-
class
|
|
136
|
+
class _DutBase(DutInterface):
|
|
137
|
+
def __init__(self, *args: t.Any, **_kwargs: t.Any) -> None: # pylint: disable=unused-argument
|
|
138
|
+
# kwargs are kept for BasePort creation and should not be forwarded
|
|
139
|
+
# to the end of MRO where object.__init__ rejects extra arguments.
|
|
140
|
+
super().__init__()
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class DutBase(VariablesMixin, DataMonitorMixin, _DutBase): # pylint: disable=too-many-public-methods
|
|
135
144
|
"""A base Dut class"""
|
|
136
145
|
|
|
137
146
|
BASE_PORT_PROXY_METHODS = list(PortInterface.__abstractmethods__)
|
|
138
147
|
|
|
139
148
|
def __init__(self, *, dut_config: DutConfig, **kwargs: t.Any) -> None:
|
|
149
|
+
# pass extra parameters to base port
|
|
150
|
+
self._kwargs = kwargs
|
|
151
|
+
if dut_config.monitors:
|
|
152
|
+
self._kwargs['monitors'] = dut_config.monitors
|
|
153
|
+
if dut_config.rx_log_callback:
|
|
154
|
+
self._kwargs['rx_log_callback'] = dut_config.rx_log_callback
|
|
155
|
+
# kwargs are kept for BasePort creation and should not be forwarded
|
|
156
|
+
# to the end of MRO where object.__init__ rejects extra arguments.
|
|
140
157
|
super().__init__(**kwargs)
|
|
141
158
|
# args and kwargs may be used by mixins
|
|
142
159
|
self._dut_config: DutConfig = dut_config
|
|
143
|
-
self._kwargs = kwargs
|
|
144
160
|
self._raw_port: t.Optional[RawPort] = None
|
|
145
161
|
self._base_port_proxy: t.Optional[BasePort] = None
|
|
146
162
|
self._dut_logger: logging.Logger = self._create_dut_logger()
|
|
@@ -235,6 +251,31 @@ class DutBase(VariablesMixin, DutInterface): # pylint: disable=too-many-public-
|
|
|
235
251
|
return self._base_port_proxy.log_file
|
|
236
252
|
return None
|
|
237
253
|
|
|
254
|
+
@property
|
|
255
|
+
def rx_log_callback(self) -> t.Optional[t.Callable[[str, bytes], None]]:
|
|
256
|
+
"""Get Current dut log file."""
|
|
257
|
+
if self._base_port_proxy:
|
|
258
|
+
return self._base_port_proxy.rx_log_callback
|
|
259
|
+
return t.cast(t.Optional[t.Callable[[str, bytes], None]], self._kwargs.get('rx_log_callback', None))
|
|
260
|
+
|
|
261
|
+
def set_rx_log_callback(self, new_callback: t.Optional[t.Callable[[str, bytes], None]]) -> None:
|
|
262
|
+
self._kwargs['rx_log_callback'] = new_callback
|
|
263
|
+
if self._base_port_proxy:
|
|
264
|
+
self._base_port_proxy.set_rx_log_callback(new_callback)
|
|
265
|
+
|
|
266
|
+
@property
|
|
267
|
+
def monitors(self) -> t.List[DataMonitor]:
|
|
268
|
+
if self._base_port_proxy:
|
|
269
|
+
return self._base_port_proxy.monitors
|
|
270
|
+
return t.cast(t.List[DataMonitor], self._kwargs.setdefault('monitors', []))
|
|
271
|
+
|
|
272
|
+
@monitors.setter
|
|
273
|
+
def monitors(self, new_monitors: t.List[DataMonitor]) -> None:
|
|
274
|
+
synced_monitors = list(new_monitors)
|
|
275
|
+
self._kwargs['monitors'] = synced_monitors
|
|
276
|
+
if self._base_port_proxy:
|
|
277
|
+
self._base_port_proxy.monitors = synced_monitors
|
|
278
|
+
|
|
238
279
|
@property
|
|
239
280
|
def name(self) -> t.Any:
|
|
240
281
|
return self.dut_config.name
|
|
@@ -34,11 +34,15 @@ class EspDut(_DefaultMixins, DutBase):
|
|
|
34
34
|
if _config.opened_port:
|
|
35
35
|
if isinstance(_config.opened_port, BasePort):
|
|
36
36
|
_base_port = _config.opened_port
|
|
37
|
+
if self._kwargs.get('monitors') is not None:
|
|
38
|
+
_base_port.monitors = self._kwargs['monitors']
|
|
39
|
+
if self._kwargs.get('rx_log_callback'):
|
|
40
|
+
_base_port.set_rx_log_callback(self._kwargs['rx_log_callback'])
|
|
37
41
|
self._close_base_port_when_exit = False
|
|
38
42
|
self._close_raw_port_when_exit = False
|
|
39
43
|
return _base_port
|
|
40
44
|
if isinstance(_config.opened_port, RawPort):
|
|
41
|
-
_base_port = BasePort(_config.opened_port, name=_config.name, log_file=_config.log_file)
|
|
45
|
+
_base_port = BasePort(_config.opened_port, name=_config.name, log_file=_config.log_file, **self._kwargs)
|
|
42
46
|
self._close_raw_port_when_exit = False
|
|
43
47
|
return _base_port
|
|
44
48
|
raise TypeError(f'Can not create dut from {type(_config.opened_port)}')
|
|
@@ -51,10 +55,10 @@ class EspDut(_DefaultMixins, DutBase):
|
|
|
51
55
|
_esp._port.timeout = _config.serial_read_timeout # pylint: disable=protected-access
|
|
52
56
|
_esp.hard_reset()
|
|
53
57
|
self._raw_port = _esp
|
|
54
|
-
return BasePort(EspSerial(self._raw_port), name=_config.name, log_file=_config.log_file)
|
|
58
|
+
return BasePort(EspSerial(self._raw_port), name=_config.name, log_file=_config.log_file, **self._kwargs)
|
|
55
59
|
# create basic serial port
|
|
56
60
|
self._raw_port = SerialExt(port=_device, baudrate=_config.baudrate, **(_config.serial_configs or {}))
|
|
57
|
-
return SerialPort(self._raw_port, name=_config.name, log_file=_config.log_file)
|
|
61
|
+
return SerialPort(self._raw_port, name=_config.name, log_file=_config.log_file, **self._kwargs)
|
|
58
62
|
|
|
59
63
|
def close(self) -> None:
|
|
60
64
|
if self._close_base_port_when_exit:
|
|
@@ -8,15 +8,17 @@ import re
|
|
|
8
8
|
import sys
|
|
9
9
|
import threading
|
|
10
10
|
import time
|
|
11
|
-
from dataclasses import dataclass
|
|
12
11
|
from typing import overload
|
|
13
12
|
|
|
14
13
|
import esptest.common.compat_typing as t
|
|
15
14
|
|
|
16
15
|
from ...common import timestamp_str, to_bytes, to_str
|
|
16
|
+
from ...common.data_monitor import DataMonitor
|
|
17
17
|
from ...common.decorators import deprecated
|
|
18
|
+
from ...config.global_config import g
|
|
18
19
|
from ...interface.port import PortInterface
|
|
19
20
|
from ...logger import get_logger
|
|
21
|
+
from .data_monitor_mixin import DataMonitorMixin
|
|
20
22
|
|
|
21
23
|
if sys.platform == 'win32':
|
|
22
24
|
import pexpect
|
|
@@ -32,6 +34,7 @@ else:
|
|
|
32
34
|
|
|
33
35
|
logger = get_logger('port')
|
|
34
36
|
NEVER_MATCHED_MAGIC_STRING = 'o6K,Q.(w+~yr~N9R'
|
|
37
|
+
PEXPECT_DEFAULT_TIMEOUT = g.PORT_EXPECT_TIMEOUT
|
|
35
38
|
|
|
36
39
|
|
|
37
40
|
class ExpectTimeout(TimeoutError):
|
|
@@ -77,21 +80,6 @@ class RawPort(metaclass=abc.ABCMeta):
|
|
|
77
80
|
T = t.TypeVar('T', bound=RawPort)
|
|
78
81
|
|
|
79
82
|
|
|
80
|
-
@dataclass
|
|
81
|
-
class SpawnConfig:
|
|
82
|
-
name: str = '' # mandatory
|
|
83
|
-
timeout: float = 30.0
|
|
84
|
-
read_interval: float = 0
|
|
85
|
-
# save rx log to log file
|
|
86
|
-
log_file: t.Optional[str] = None
|
|
87
|
-
# using logger.info for logs
|
|
88
|
-
logger: logging.Logger = logger
|
|
89
|
-
# callback cb(data,name)
|
|
90
|
-
tx_callbacks: t.Optional[t.List[t.Callable[[bytes, str], None]]] = None
|
|
91
|
-
rx_callbacks: t.Optional[t.List[t.Callable[[bytes, str], None]]] = None
|
|
92
|
-
# TODO: monitors
|
|
93
|
-
|
|
94
|
-
|
|
95
83
|
class PortSpawn(SpawnBase, t.Generic[T]):
|
|
96
84
|
"""Create a new class for pexpect with port read()/write() method.
|
|
97
85
|
|
|
@@ -110,7 +98,7 @@ class PortSpawn(SpawnBase, t.Generic[T]):
|
|
|
110
98
|
raw_port: T,
|
|
111
99
|
name: str = '',
|
|
112
100
|
log_file: t.Optional[str] = None,
|
|
113
|
-
timeout: float =
|
|
101
|
+
timeout: float = PEXPECT_DEFAULT_TIMEOUT,
|
|
114
102
|
**kwargs: t.Any,
|
|
115
103
|
) -> None:
|
|
116
104
|
"""PortSpawn for pexpect
|
|
@@ -123,6 +111,7 @@ class PortSpawn(SpawnBase, t.Generic[T]):
|
|
|
123
111
|
"""
|
|
124
112
|
super().__init__(timeout=timeout)
|
|
125
113
|
assert isinstance(raw_port, RawPort)
|
|
114
|
+
self.maxread = kwargs.get('maxread', g.PORT_SPAWN_MAXREAD)
|
|
126
115
|
self.name = name
|
|
127
116
|
self._raw_port = raw_port
|
|
128
117
|
if not self.name and hasattr(self.raw_port, 'name'):
|
|
@@ -138,10 +127,23 @@ class PortSpawn(SpawnBase, t.Generic[T]):
|
|
|
138
127
|
# Create a new thread to read data from serial port
|
|
139
128
|
self._read_queue: queue.Queue = queue.Queue()
|
|
140
129
|
self._read_thread_stop_event = threading.Event()
|
|
130
|
+
# callbacks
|
|
131
|
+
self._rx_log_callback: t.Optional[t.Callable[[str, bytes], None]] = kwargs.get('rx_log_callback', None)
|
|
132
|
+
# monitors
|
|
133
|
+
self._monitors: t.Optional[t.List[DataMonitor]] = kwargs.get('monitors', None)
|
|
134
|
+
self._serial_error_reconnect_count_left = max(0, int(g.ALLOW_SERIAL_ERROR_RECONNECT_COUNT))
|
|
141
135
|
self._read_thread = threading.Thread(target=self._read_incoming, name=f'Spawn_{self.name}')
|
|
142
136
|
self._read_thread.daemon = True
|
|
143
137
|
self._read_thread.start()
|
|
144
|
-
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def receive_callback(self) -> t.Optional[t.Callable[[str, bytes], None]]:
|
|
141
|
+
return self._rx_log_callback
|
|
142
|
+
|
|
143
|
+
@receive_callback.setter
|
|
144
|
+
@deprecated('set receive_callback directly is deprecated, use rx_log_callback instead')
|
|
145
|
+
def receive_callback(self, new_callback: t.Optional[t.Callable[[str, bytes], None]]) -> None:
|
|
146
|
+
self._rx_log_callback = new_callback
|
|
145
147
|
|
|
146
148
|
@property
|
|
147
149
|
def raw_port(self) -> T:
|
|
@@ -189,6 +191,32 @@ class PortSpawn(SpawnBase, t.Generic[T]):
|
|
|
189
191
|
else:
|
|
190
192
|
self.logger.debug(f'[{self.name}]: {to_str(data_to_write)}')
|
|
191
193
|
|
|
194
|
+
def _try_reconnect_after_error(self, err: Exception) -> bool:
|
|
195
|
+
if self._serial_error_reconnect_count_left <= 0:
|
|
196
|
+
return False
|
|
197
|
+
raw_port_close = getattr(self.raw_port, 'close', None)
|
|
198
|
+
raw_port_open = getattr(self.raw_port, 'open', None)
|
|
199
|
+
if not callable(raw_port_close) or not callable(raw_port_open):
|
|
200
|
+
self.logger.warning(f'Skip serial reconnect after serial error on {self.name}: raw port missing close/open')
|
|
201
|
+
return False
|
|
202
|
+
try:
|
|
203
|
+
self._serial_error_reconnect_count_left -= 1
|
|
204
|
+
raw_port_close()
|
|
205
|
+
# Keep a short gap to avoid immediate open/read race on some serial drivers.
|
|
206
|
+
time.sleep(0.1)
|
|
207
|
+
raw_port_open()
|
|
208
|
+
self.logger.warning(
|
|
209
|
+
f'{self.name} got serial error, reopened serial, '
|
|
210
|
+
f'reconnect_left={self._serial_error_reconnect_count_left}'
|
|
211
|
+
)
|
|
212
|
+
return True
|
|
213
|
+
except Exception as reconnect_err: # pylint: disable=broad-except
|
|
214
|
+
self.logger.exception(
|
|
215
|
+
f'{self.name} failed to reconnect serial after error '
|
|
216
|
+
f'{type(err)}: {str(err)}, reconnect_error={type(reconnect_err)}: {str(reconnect_err)}'
|
|
217
|
+
)
|
|
218
|
+
return False
|
|
219
|
+
|
|
192
220
|
def _read_incoming(self) -> None:
|
|
193
221
|
"""Running in a thread to read serial output and save to data cache."""
|
|
194
222
|
self.logger.debug(f'Start serial {self.name} read thread.')
|
|
@@ -204,15 +232,23 @@ class PortSpawn(SpawnBase, t.Generic[T]):
|
|
|
204
232
|
# some port instances do not support changing read timeout, therefore use default timeout of the
|
|
205
233
|
new_data = self.raw_port.read_bytes(timeout=self.read_timeout)
|
|
206
234
|
except Exception as e: # pylint: disable=W0718
|
|
207
|
-
self._log(to_bytes(f'
|
|
208
|
-
self.
|
|
209
|
-
|
|
210
|
-
|
|
235
|
+
self._log(to_bytes(f'PortReadError {type(e)}: {str(e)}'), 'read')
|
|
236
|
+
self.logger.exception(f'{self.name} port read error {type(e)}: {str(e)}')
|
|
237
|
+
time.sleep(0.01) # avoid busy loop
|
|
238
|
+
if self._try_reconnect_after_error(e):
|
|
239
|
+
self.logger.critical(f'{self.name} reconnected after error {type(e)}: {str(e)}')
|
|
240
|
+
new_data = f'[PortException] reconnected after error {type(e)}: {str(e)}\n'.encode()
|
|
241
|
+
else:
|
|
242
|
+
self._write_port_log(to_bytes(f'[PortException] {type(e)}: {str(e)}\n'))
|
|
243
|
+
return
|
|
211
244
|
if new_data:
|
|
212
245
|
self._read_queue.put(new_data)
|
|
213
|
-
if self.
|
|
246
|
+
if self._rx_log_callback:
|
|
214
247
|
# https://stackoverflow.com/questions/69732212/pylint-self-xxx-is-not-callable
|
|
215
|
-
self.
|
|
248
|
+
self._rx_log_callback(self.name, new_data) # pylint: disable=E1102
|
|
249
|
+
if self._monitors:
|
|
250
|
+
for monitor in self._monitors:
|
|
251
|
+
monitor.append_data(self.name, new_data)
|
|
216
252
|
# the last line may be cached, to make the file more readable after adding timestamp
|
|
217
253
|
# always check need write to file or not whether there's new data
|
|
218
254
|
self._write_port_log(new_data)
|
|
@@ -254,6 +290,9 @@ class PortSpawn(SpawnBase, t.Generic[T]):
|
|
|
254
290
|
except queue.Empty:
|
|
255
291
|
break
|
|
256
292
|
time_left = t0 + timeout - time.time()
|
|
293
|
+
# clear older data cache if it is larger than 2x limit
|
|
294
|
+
if len(self._data_cache) >= g.DATA_CACHE_SIZE_LIMIT * 2:
|
|
295
|
+
self._data_cache = self._data_cache[-g.DATA_CACHE_SIZE_LIMIT :]
|
|
257
296
|
# Returned data should not more than given size.
|
|
258
297
|
if self._data_cache:
|
|
259
298
|
ret_data = self._data_cache[:size]
|
|
@@ -274,7 +313,8 @@ class PortSpawn(SpawnBase, t.Generic[T]):
|
|
|
274
313
|
self._read_thread_stop_event.set()
|
|
275
314
|
self._read_thread.join()
|
|
276
315
|
self._read_queue.empty()
|
|
277
|
-
self.
|
|
316
|
+
self._rx_log_callback = None
|
|
317
|
+
self._monitors = []
|
|
278
318
|
self._data_cache = b''
|
|
279
319
|
self._line_cache = b''
|
|
280
320
|
|
|
@@ -300,7 +340,14 @@ def handle_expect_timeout(func: t.Callable) -> t.Callable:
|
|
|
300
340
|
return wrap
|
|
301
341
|
|
|
302
342
|
|
|
303
|
-
class
|
|
343
|
+
class _BasePort(PortInterface):
|
|
344
|
+
def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: # pylint: disable=unused-argument
|
|
345
|
+
# kwargs are kept for BasePort creation and should not be forwarded
|
|
346
|
+
# to the end of MRO where object.__init__ rejects extra arguments.
|
|
347
|
+
super().__init__()
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
class BasePort(DataMonitorMixin, _BasePort, t.Generic[T]): # pylint: disable=too-many-public-methods
|
|
304
351
|
"""A class to simply port methods for all devices / shell / sockets to similar usage
|
|
305
352
|
|
|
306
353
|
- Create receive thread and pexpect spawn process for data read/expect
|
|
@@ -313,7 +360,6 @@ class BasePort(PortInterface, t.Generic[T]):
|
|
|
313
360
|
ExceptionPexpect,
|
|
314
361
|
)
|
|
315
362
|
INIT_START_REDIRECT_THREAD: bool = True
|
|
316
|
-
PEXPECT_DEFAULT_TIMEOUT: float = 30
|
|
317
363
|
|
|
318
364
|
def __init__(
|
|
319
365
|
self,
|
|
@@ -322,6 +368,7 @@ class BasePort(PortInterface, t.Generic[T]):
|
|
|
322
368
|
log_file: str = '',
|
|
323
369
|
**kwargs: t.Any,
|
|
324
370
|
) -> None:
|
|
371
|
+
super().__init__(**kwargs)
|
|
325
372
|
self._raw_port = raw_port
|
|
326
373
|
self._name = name
|
|
327
374
|
self._log_file = log_file
|
|
@@ -332,7 +379,7 @@ class BasePort(PortInterface, t.Generic[T]):
|
|
|
332
379
|
self._close_redirect_thread_when_exit = kwargs['close_redirect_thread_when_exit']
|
|
333
380
|
# redirect thread (pexpect spawn)
|
|
334
381
|
self.expect_timeout_exceptions = self.EXPECT_TIMEOUT_EXCEPTIONS
|
|
335
|
-
self.timeout =
|
|
382
|
+
self.timeout = kwargs.get('timeout', PEXPECT_DEFAULT_TIMEOUT)
|
|
336
383
|
self._pexpect_spawn: t.Optional[PortSpawn] = None
|
|
337
384
|
# logger
|
|
338
385
|
self._logger = self._get_logger()
|
|
@@ -408,6 +455,27 @@ class BasePort(PortInterface, t.Generic[T]):
|
|
|
408
455
|
self._pexpect_spawn.log_file = new_log_file
|
|
409
456
|
self._log_file = new_log_file
|
|
410
457
|
|
|
458
|
+
@property
|
|
459
|
+
def rx_log_callback(self) -> t.Optional[t.Callable[[str, bytes], None]]:
|
|
460
|
+
"""Get Current dut log file."""
|
|
461
|
+
return t.cast(t.Optional[t.Callable[[str, bytes], None]], self._kwargs.get('rx_log_callback', None))
|
|
462
|
+
|
|
463
|
+
def set_rx_log_callback(self, new_callback: t.Optional[t.Callable[[str, bytes], None]]) -> None:
|
|
464
|
+
self._kwargs['rx_log_callback'] = new_callback
|
|
465
|
+
if self._pexpect_spawn:
|
|
466
|
+
self._pexpect_spawn._rx_log_callback = new_callback # pylint: disable=protected-access
|
|
467
|
+
|
|
468
|
+
@property
|
|
469
|
+
def monitors(self) -> t.List[DataMonitor]:
|
|
470
|
+
return t.cast(t.List[DataMonitor], self._kwargs.setdefault('monitors', []))
|
|
471
|
+
|
|
472
|
+
@monitors.setter
|
|
473
|
+
def monitors(self, new_monitors: t.List[DataMonitor]) -> None:
|
|
474
|
+
synced_monitors = list(new_monitors)
|
|
475
|
+
self._kwargs['monitors'] = synced_monitors
|
|
476
|
+
if self._pexpect_spawn:
|
|
477
|
+
self._pexpect_spawn._monitors = synced_monitors # pylint: disable=protected-access
|
|
478
|
+
|
|
411
479
|
@property
|
|
412
480
|
def spawn(self) -> t.Optional[PortSpawn]:
|
|
413
481
|
"""Allow the use of pexpect spawn enhancements, if pexpect process is available"""
|
|
@@ -419,7 +487,7 @@ class BasePort(PortInterface, t.Generic[T]):
|
|
|
419
487
|
return
|
|
420
488
|
self._init_log_file()
|
|
421
489
|
self._pexpect_spawn = PortSpawn(
|
|
422
|
-
self.raw_port, self.name, self.log_file,
|
|
490
|
+
self.raw_port, self.name, self.log_file, PEXPECT_DEFAULT_TIMEOUT, **self._kwargs
|
|
423
491
|
)
|
|
424
492
|
|
|
425
493
|
def stop_redirect_thread(self) -> bool:
|
|
@@ -455,13 +523,13 @@ class BasePort(PortInterface, t.Generic[T]):
|
|
|
455
523
|
raise NotImplementedError()
|
|
456
524
|
|
|
457
525
|
@overload
|
|
458
|
-
def expect(self, pattern: str, timeout: float =
|
|
526
|
+
def expect(self, pattern: str, timeout: float = PEXPECT_DEFAULT_TIMEOUT) -> None: ...
|
|
459
527
|
@overload
|
|
460
|
-
def expect(self, pattern: bytes, timeout: float =
|
|
528
|
+
def expect(self, pattern: bytes, timeout: float = PEXPECT_DEFAULT_TIMEOUT) -> None: ...
|
|
461
529
|
@overload
|
|
462
|
-
def expect(self, pattern: 're.Pattern[str]', timeout: float =
|
|
530
|
+
def expect(self, pattern: 're.Pattern[str]', timeout: float = PEXPECT_DEFAULT_TIMEOUT) -> 're.Match[str]': ...
|
|
463
531
|
@overload
|
|
464
|
-
def expect(self, pattern: 're.Pattern[bytes]', timeout: float =
|
|
532
|
+
def expect(self, pattern: 're.Pattern[bytes]', timeout: float = PEXPECT_DEFAULT_TIMEOUT) -> 're.Match[bytes]': ...
|
|
465
533
|
|
|
466
534
|
@handle_expect_timeout
|
|
467
535
|
def expect(self, pattern, timeout=PEXPECT_DEFAULT_TIMEOUT): # type: ignore
|
|
@@ -473,6 +541,12 @@ class BasePort(PortInterface, t.Generic[T]):
|
|
|
473
541
|
If the pattern type is re.Pattern, this method will return a re.Match object if the pattern is matched.
|
|
474
542
|
Can read all output data by pattern=re.compile('.+', re.DOTALL)
|
|
475
543
|
|
|
544
|
+
Note:
|
|
545
|
+
When matching very long data in a single read, pexpect may truncate the buffer
|
|
546
|
+
due to its ``maxread`` limit. If the expected pattern can
|
|
547
|
+
span a large chunk of output, increase ``maxread`` on the underlying pexpect
|
|
548
|
+
spawn accordingly.
|
|
549
|
+
|
|
476
550
|
Args:
|
|
477
551
|
pattern (t.Union[str, bytes, re.Pattern]): pattern to match
|
|
478
552
|
timeout (int, optional): seconds of waiting for new data if match failed. Defaults to 30s.
|
|
@@ -518,18 +592,25 @@ class BasePort(PortInterface, t.Generic[T]):
|
|
|
518
592
|
"""
|
|
519
593
|
buffer = b''
|
|
520
594
|
if flush:
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
595
|
+
while True:
|
|
596
|
+
new_data = b''
|
|
597
|
+
# pexpect may return empty bytes if b'(.*)' is used
|
|
598
|
+
try:
|
|
599
|
+
match = self.expect(re.compile(b'(.+)', re.DOTALL), timeout=0)
|
|
600
|
+
assert match
|
|
601
|
+
new_data = match.group(0)
|
|
602
|
+
except TimeoutError:
|
|
603
|
+
pass
|
|
604
|
+
if not new_data:
|
|
605
|
+
break
|
|
606
|
+
buffer += new_data
|
|
528
607
|
else:
|
|
529
|
-
#
|
|
608
|
+
# update spawn buffer
|
|
530
609
|
assert self._pexpect_spawn
|
|
531
610
|
self._pexpect_spawn.expect_exact(pexpect.TIMEOUT, timeout=0)
|
|
532
611
|
buffer = to_bytes(self._pexpect_spawn.buffer)
|
|
612
|
+
if hasattr(self._pexpect_spawn, 'data_cache'):
|
|
613
|
+
buffer += to_bytes(self._pexpect_spawn.data_cache)
|
|
533
614
|
assert isinstance(buffer, bytes)
|
|
534
615
|
return buffer
|
|
535
616
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
import esptest.common.compat_typing as t
|
|
4
|
+
|
|
5
|
+
from ...common.data_monitor import DataMonitor
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
|
|
9
|
+
class _SupportsMonitors(t.Protocol):
|
|
10
|
+
@property
|
|
11
|
+
def monitors(self) -> t.List[DataMonitor]: ...
|
|
12
|
+
|
|
13
|
+
@monitors.setter
|
|
14
|
+
def monitors(self, new_monitors: t.List[DataMonitor]) -> None: ...
|
|
15
|
+
|
|
16
|
+
else:
|
|
17
|
+
_SupportsMonitors = object
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class DataMonitorMixin(_SupportsMonitors):
|
|
21
|
+
def add_monitor(self, monitor: DataMonitor) -> None:
|
|
22
|
+
new_monitors = list(self.monitors)
|
|
23
|
+
if monitor in new_monitors:
|
|
24
|
+
return
|
|
25
|
+
new_monitors.append(monitor)
|
|
26
|
+
self.monitors = new_monitors
|
|
27
|
+
|
|
28
|
+
def remove_monitor(self, monitor: DataMonitor) -> None:
|
|
29
|
+
new_monitors = list(self.monitors)
|
|
30
|
+
if monitor not in new_monitors:
|
|
31
|
+
return
|
|
32
|
+
new_monitors.remove(monitor)
|
|
33
|
+
self.monitors = new_monitors
|
|
34
|
+
|
|
35
|
+
def clear_monitors(self) -> None:
|
|
36
|
+
self.monitors = []
|
|
@@ -87,9 +87,9 @@ class SerialPortMixin(MixinBase):
|
|
|
87
87
|
elif issubclass(original_type, SerialBase):
|
|
88
88
|
raw_port.__class__ = serial_add_mixin(original_type)
|
|
89
89
|
|
|
90
|
-
def __init__(self, raw_port: t.Any, name: str, log_file: str = '') -> None:
|
|
90
|
+
def __init__(self, raw_port: t.Any, name: str, log_file: str = '', **kwargs: t.Any) -> None:
|
|
91
91
|
self._add_mixin_by_type(raw_port)
|
|
92
|
-
super().__init__(raw_port, name, log_file)
|
|
92
|
+
super().__init__(raw_port, name, log_file, **kwargs)
|
|
93
93
|
self._serial_config: t.Dict[str, t.Any] = {}
|
|
94
94
|
|
|
95
95
|
def start_redirect_thread(self) -> None:
|
|
@@ -11,6 +11,7 @@ import psutil
|
|
|
11
11
|
import esptest.common.compat_typing as t
|
|
12
12
|
|
|
13
13
|
from ...common.shell import ensure_windows_env
|
|
14
|
+
from ...config.global_config import g
|
|
14
15
|
from ...logger import get_logger
|
|
15
16
|
from .base_port import BasePort, RawPort
|
|
16
17
|
|
|
@@ -270,7 +271,8 @@ class PexpectPort(BasePort[InvalidRaw]):
|
|
|
270
271
|
self._init_log_file()
|
|
271
272
|
env = os.environ.copy()
|
|
272
273
|
env['PYTHONUNBUFFERED'] = 'true' # for python scripts, disable output buffering
|
|
273
|
-
|
|
274
|
+
maxread = self._kwargs.get('maxread', g.PORT_SPAWN_MAXREAD)
|
|
275
|
+
self._pexpect_spawn = pexpect.spawn(self._cmd, maxread=maxread, echo=False, env=env) # type: ignore
|
|
274
276
|
self._pexpect_spawn.logfile = self.log_file_f # type: ignore
|
|
275
277
|
# self._pexpect_spawn.delaybeforesend = 0.001
|
|
276
278
|
|