pyxcp 0.23.8__cp313-cp313-macosx_11_0_arm64.whl → 0.25.7__cp313-cp313-macosx_11_0_arm64.whl
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.
- pyxcp/__init__.py +1 -1
- pyxcp/cmdline.py +14 -29
- pyxcp/config/__init__.py +1257 -1258
- pyxcp/cpp_ext/aligned_buffer.hpp +168 -0
- pyxcp/cpp_ext/bin.hpp +7 -6
- pyxcp/cpp_ext/cpp_ext.cpython-310-darwin.so +0 -0
- pyxcp/cpp_ext/cpp_ext.cpython-311-darwin.so +0 -0
- pyxcp/cpp_ext/cpp_ext.cpython-312-darwin.so +0 -0
- pyxcp/cpp_ext/cpp_ext.cpython-313-darwin.so +0 -0
- pyxcp/cpp_ext/daqlist.hpp +241 -73
- pyxcp/cpp_ext/extension_wrapper.cpp +123 -15
- pyxcp/cpp_ext/framing.hpp +360 -0
- pyxcp/cpp_ext/helper.hpp +280 -280
- pyxcp/cpp_ext/mcobject.hpp +248 -246
- pyxcp/cpp_ext/sxi_framing.hpp +332 -0
- pyxcp/daq_stim/__init__.py +145 -67
- pyxcp/daq_stim/optimize/binpacking.py +2 -2
- pyxcp/daq_stim/scheduler.cpp +8 -8
- pyxcp/errormatrix.py +2 -2
- pyxcp/examples/run_daq.py +5 -4
- pyxcp/examples/xcp_policy.py +6 -6
- pyxcp/examples/xcp_read_benchmark.py +2 -2
- pyxcp/examples/xcp_skel.py +1 -2
- pyxcp/examples/xcp_unlock.py +10 -12
- pyxcp/examples/xcp_user_supplied_driver.py +1 -2
- pyxcp/examples/xcphello.py +2 -15
- pyxcp/examples/xcphello_recorder.py +2 -2
- pyxcp/master/__init__.py +1 -0
- pyxcp/master/errorhandler.py +134 -4
- pyxcp/master/master.py +823 -252
- pyxcp/recorder/.idea/.gitignore +8 -0
- pyxcp/recorder/.idea/misc.xml +4 -0
- pyxcp/recorder/.idea/modules.xml +8 -0
- pyxcp/recorder/.idea/recorder.iml +6 -0
- pyxcp/recorder/.idea/sonarlint/issuestore/3/8/3808afc69ac1edb9d760000a2f137335b1b99728 +7 -0
- pyxcp/recorder/.idea/sonarlint/issuestore/9/a/9a2aa4db38d3115ed60da621e012c0efc0172aae +0 -0
- pyxcp/recorder/.idea/sonarlint/issuestore/b/4/b49006702b459496a8e8c94ebe60947108361b91 +0 -0
- pyxcp/recorder/.idea/sonarlint/issuestore/index.pb +7 -0
- pyxcp/recorder/.idea/sonarlint/securityhotspotstore/3/8/3808afc69ac1edb9d760000a2f137335b1b99728 +0 -0
- pyxcp/recorder/.idea/sonarlint/securityhotspotstore/9/a/9a2aa4db38d3115ed60da621e012c0efc0172aae +0 -0
- pyxcp/recorder/.idea/sonarlint/securityhotspotstore/b/4/b49006702b459496a8e8c94ebe60947108361b91 +0 -0
- pyxcp/recorder/.idea/sonarlint/securityhotspotstore/index.pb +7 -0
- pyxcp/recorder/.idea/vcs.xml +10 -0
- pyxcp/recorder/__init__.py +96 -98
- pyxcp/recorder/converter/__init__.py +4 -10
- pyxcp/recorder/reader.hpp +138 -139
- pyxcp/recorder/reco.py +1 -0
- pyxcp/recorder/rekorder.cpython-310-darwin.so +0 -0
- pyxcp/recorder/rekorder.cpython-311-darwin.so +0 -0
- pyxcp/recorder/rekorder.cpython-312-darwin.so +0 -0
- pyxcp/recorder/rekorder.cpython-313-darwin.so +0 -0
- pyxcp/recorder/rekorder.hpp +274 -274
- pyxcp/recorder/unfolder.hpp +1354 -1319
- pyxcp/recorder/wrap.cpp +184 -183
- pyxcp/recorder/writer.hpp +302 -302
- pyxcp/scripts/xcp_daq_recorder.py +54 -0
- pyxcp/scripts/xcp_fetch_a2l.py +2 -2
- pyxcp/scripts/xcp_id_scanner.py +1 -2
- pyxcp/scripts/xcp_info.py +66 -51
- pyxcp/scripts/xcp_profile.py +1 -2
- pyxcp/tests/test_daq.py +1 -1
- pyxcp/tests/test_framing.py +262 -0
- pyxcp/tests/test_master.py +210 -100
- pyxcp/tests/test_transport.py +138 -42
- pyxcp/timing.py +1 -1
- pyxcp/transport/__init__.py +8 -5
- pyxcp/transport/base.py +70 -180
- pyxcp/transport/can.py +58 -7
- pyxcp/transport/eth.py +32 -15
- pyxcp/transport/hdf5_policy.py +167 -0
- pyxcp/transport/sxi.py +126 -52
- pyxcp/transport/transport_ext.cpython-310-darwin.so +0 -0
- pyxcp/transport/transport_ext.cpython-311-darwin.so +0 -0
- pyxcp/transport/transport_ext.cpython-312-darwin.so +0 -0
- pyxcp/transport/transport_ext.cpython-313-darwin.so +0 -0
- pyxcp/transport/transport_ext.hpp +214 -0
- pyxcp/transport/transport_wrapper.cpp +249 -0
- pyxcp/transport/usb_transport.py +47 -31
- pyxcp/types.py +0 -13
- pyxcp/{utils.py → utils/__init__.py} +1 -2
- pyxcp/utils/cli.py +78 -0
- {pyxcp-0.23.8.dist-info → pyxcp-0.25.7.dist-info}/METADATA +4 -2
- pyxcp-0.25.7.dist-info/RECORD +158 -0
- {pyxcp-0.23.8.dist-info → pyxcp-0.25.7.dist-info}/WHEEL +1 -1
- pyxcp/examples/conf_sxi.json +0 -9
- pyxcp/examples/conf_sxi.toml +0 -7
- pyxcp-0.23.8.dist-info/RECORD +0 -135
- {pyxcp-0.23.8.dist-info → pyxcp-0.25.7.dist-info}/entry_points.txt +0 -0
- {pyxcp-0.23.8.dist-info → pyxcp-0.25.7.dist-info/licenses}/LICENSE +0 -0
pyxcp/daq_stim/scheduler.cpp
CHANGED
|
@@ -10,10 +10,10 @@ VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired) {
|
|
|
10
10
|
std::printf("TimerRoutine lpParam is NULL\n");
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
const auto* param = static_cast<const int*>(lpParam);
|
|
15
15
|
std::printf("Timer routine called. Parameter is %d.\n", *param);
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
if (TimerOrWaitFired) {
|
|
18
18
|
std::printf("The wait timed out.\n");
|
|
19
19
|
} else {
|
|
@@ -30,10 +30,10 @@ namespace {
|
|
|
30
30
|
|
|
31
31
|
#if defined(_M_X64) || defined(_M_IX86) || defined(__SSE__)
|
|
32
32
|
#include <xmmintrin.h>
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
void mul4_vectorized(float* ptr) {
|
|
35
35
|
if (ptr == nullptr) return;
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
__m128 f = _mm_loadu_ps(ptr);
|
|
38
38
|
f = _mm_mul_ps(f, f);
|
|
39
39
|
_mm_storeu_ps(ptr, f);
|
|
@@ -41,10 +41,10 @@ namespace {
|
|
|
41
41
|
|
|
42
42
|
#elif defined(_M_ARM64) || defined(__ARM_NEON)
|
|
43
43
|
#include <arm_neon.h>
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
void mul4_vectorized(float* ptr) {
|
|
46
46
|
if (ptr == nullptr) return;
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
float32x4_t f = vld1q_f32(ptr);
|
|
49
49
|
f = vmulq_f32(f, f);
|
|
50
50
|
vst1q_f32(ptr, f);
|
|
@@ -54,9 +54,9 @@ namespace {
|
|
|
54
54
|
// Scalar fallback
|
|
55
55
|
void mul4_vectorized(float* ptr) {
|
|
56
56
|
if (ptr == nullptr) return;
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
for (size_t i = 0; i < VECTOR_SIZE; ++i) {
|
|
59
59
|
ptr[i] *= ptr[i];
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
-
#endif
|
|
62
|
+
#endif
|
pyxcp/errormatrix.py
CHANGED
pyxcp/examples/run_daq.py
CHANGED
|
@@ -129,8 +129,9 @@ else:
|
|
|
129
129
|
),
|
|
130
130
|
]
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
# daq_parser =
|
|
132
|
+
|
|
133
|
+
# daq_parser = DaqToCsv(DAQ_LISTS) # Record to CSV file(s).
|
|
134
|
+
daq_parser = DaqRecorder(DAQ_LISTS, "run_daq_21092025_01", 8) # Record to ".xmraw" file.
|
|
134
135
|
|
|
135
136
|
with ap.run(policy=daq_parser) as x:
|
|
136
137
|
try:
|
|
@@ -149,8 +150,8 @@ with ap.run(policy=daq_parser) as x:
|
|
|
149
150
|
print("start DAQ lists.")
|
|
150
151
|
daq_parser.start() # Start DAQ lists.
|
|
151
152
|
|
|
152
|
-
time.sleep(2.0 * 60.0)
|
|
153
|
-
|
|
153
|
+
# time.sleep(2.0 * 60.0)
|
|
154
|
+
time.sleep(0.25 * 60.0 * 60.0) # Run for 15 minutes.
|
|
154
155
|
|
|
155
156
|
print("Stop DAQ....")
|
|
156
157
|
daq_parser.stop() # Stop DAQ lists.
|
pyxcp/examples/xcp_policy.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
|
-
"""Demostrates how to use frame recording policies including recorder extension.
|
|
3
|
-
|
|
2
|
+
"""Demostrates how to use frame recording policies including recorder extension."""
|
|
3
|
+
|
|
4
4
|
from pprint import pprint
|
|
5
5
|
|
|
6
6
|
from pyxcp.cmdline import ArgumentParser
|
|
@@ -11,11 +11,11 @@ ap = ArgumentParser(description="pyXCP frame recording policy example.")
|
|
|
11
11
|
|
|
12
12
|
LOG_FILE = "pyxcp"
|
|
13
13
|
|
|
14
|
-
policy = FrameRecorderPolicy(LOG_FILE)
|
|
15
|
-
use_recorder = True
|
|
14
|
+
# policy = FrameRecorderPolicy(LOG_FILE)
|
|
15
|
+
# use_recorder = True
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
policy = StdoutPolicy() # You may also try this one.
|
|
18
|
+
use_recorder = False
|
|
19
19
|
|
|
20
20
|
with ap.run(policy=policy) as x:
|
|
21
21
|
x.connect()
|
pyxcp/examples/xcp_skel.py
CHANGED
pyxcp/examples/xcp_unlock.py
CHANGED
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
|
-
"""Very basic hello-world example.
|
|
3
|
-
"""
|
|
4
|
-
from pyxcp.cmdline import ArgumentParser
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
"""
|
|
8
|
-
"""
|
|
2
|
+
"""Very basic hello-world example."""
|
|
9
3
|
|
|
4
|
+
import argparse
|
|
10
5
|
|
|
11
|
-
|
|
12
|
-
if args.sk_dll:
|
|
13
|
-
master.seedNKeyDLL = args.sk_dll
|
|
6
|
+
from pyxcp.cmdline import ArgumentParser
|
|
14
7
|
|
|
15
8
|
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
parser = argparse.ArgumentParser(description="XCP unlock example")
|
|
10
|
+
parser.add_argument(
|
|
18
11
|
"-s",
|
|
19
12
|
"--sk-dll",
|
|
20
13
|
dest="sk_dll",
|
|
@@ -23,7 +16,12 @@ ap.parser.add_argument(
|
|
|
23
16
|
default=None,
|
|
24
17
|
)
|
|
25
18
|
|
|
19
|
+
ap = ArgumentParser(parser)
|
|
20
|
+
|
|
26
21
|
with ap.run() as x:
|
|
22
|
+
if ap.args.sk_dll:
|
|
23
|
+
x.seedNKeyDLL = ap.args.sk_dll
|
|
24
|
+
|
|
27
25
|
x.connect()
|
|
28
26
|
|
|
29
27
|
print("")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
2
|
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Optional
|
|
4
4
|
|
|
5
5
|
import can
|
|
6
6
|
|
|
@@ -9,7 +9,6 @@ from pyxcp.transport.can import CanInterfaceBase
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class CustomCANInterface(CanInterfaceBase):
|
|
12
|
-
|
|
13
12
|
def init(self):
|
|
14
13
|
"""Initialize the CAN interface here."""
|
|
15
14
|
|
pyxcp/examples/xcphello.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
2
|
"""Very basic hello-world example."""
|
|
3
|
+
|
|
3
4
|
from pprint import pprint
|
|
4
5
|
|
|
5
6
|
from pyxcp.cmdline import ArgumentParser
|
|
@@ -9,21 +10,7 @@ from pyxcp.utils import decode_bytes
|
|
|
9
10
|
daq_info = False
|
|
10
11
|
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
global daq_info
|
|
14
|
-
if args.daq_info:
|
|
15
|
-
daq_info = True
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
ap = ArgumentParser(description="pyXCP hello world.", callout=callout)
|
|
19
|
-
ap.parser.add_argument(
|
|
20
|
-
"-d",
|
|
21
|
-
"--daq-info",
|
|
22
|
-
dest="daq_info",
|
|
23
|
-
help="Display DAQ-info",
|
|
24
|
-
default=False,
|
|
25
|
-
action="store_true",
|
|
26
|
-
)
|
|
13
|
+
ap = ArgumentParser(description="pyXCP hello world.")
|
|
27
14
|
|
|
28
15
|
with ap.run() as x:
|
|
29
16
|
x.connect()
|
pyxcp/master/__init__.py
CHANGED
pyxcp/master/errorhandler.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
|
-
"""Implements error-handling according to XCP spec.
|
|
3
|
-
|
|
2
|
+
"""Implements error-handling according to XCP spec."""
|
|
3
|
+
|
|
4
4
|
import functools
|
|
5
5
|
import logging
|
|
6
6
|
import threading
|
|
@@ -18,7 +18,6 @@ from pyxcp.types import COMMAND_CATEGORIES, XcpError, XcpResponseError, XcpTimeo
|
|
|
18
18
|
handle_errors = True # enable/disable XCP error-handling.
|
|
19
19
|
|
|
20
20
|
# Thread-local flag to suppress logging for expected XCP negative responses
|
|
21
|
-
import threading
|
|
22
21
|
|
|
23
22
|
|
|
24
23
|
_thread_flags = threading.local()
|
|
@@ -240,10 +239,141 @@ class Handler:
|
|
|
240
239
|
return ""
|
|
241
240
|
|
|
242
241
|
def _append_diag(self, msg: str) -> str:
|
|
242
|
+
# Suppress diagnostics entirely when XCP error logging is suppressed (e.g., try_command probing)
|
|
243
|
+
if is_suppress_xcp_error_log():
|
|
244
|
+
return msg
|
|
243
245
|
if not self._diagnostics_enabled():
|
|
244
246
|
return msg
|
|
245
247
|
diag = self._build_transport_diagnostics()
|
|
246
|
-
|
|
248
|
+
if not diag:
|
|
249
|
+
return msg
|
|
250
|
+
# Prefer a Rich-formatted table for compact, readable diagnostics.
|
|
251
|
+
try:
|
|
252
|
+
header = "--- Diagnostics (for troubleshooting) ---"
|
|
253
|
+
body = diag
|
|
254
|
+
if "\n" in diag and diag.startswith("--- Diagnostics"):
|
|
255
|
+
header, body = diag.split("\n", 1)
|
|
256
|
+
|
|
257
|
+
# Try to parse the structured JSON body produced by transports
|
|
258
|
+
import json as _json # Local import to avoid hard dependency at module import time
|
|
259
|
+
|
|
260
|
+
payload = _json.loads(body)
|
|
261
|
+
transport_params = payload.get("transport_params") or {}
|
|
262
|
+
last_pdus = payload.get("last_pdus") or []
|
|
263
|
+
|
|
264
|
+
# Try to use rich if available
|
|
265
|
+
try:
|
|
266
|
+
from rich.console import Console
|
|
267
|
+
from rich.table import Table
|
|
268
|
+
from rich.panel import Panel
|
|
269
|
+
from textwrap import shorten
|
|
270
|
+
|
|
271
|
+
console = Console(file=None, force_terminal=False, width=120, record=True, markup=False)
|
|
272
|
+
|
|
273
|
+
# Transport parameters table
|
|
274
|
+
tp_table = Table(title="Transport Parameters", title_style="bold", show_header=True, header_style="bold magenta")
|
|
275
|
+
tp_table.add_column("Key", style="cyan", no_wrap=True)
|
|
276
|
+
tp_table.add_column("Value", style="white")
|
|
277
|
+
from rich.markup import escape as _escape
|
|
278
|
+
|
|
279
|
+
for k, v in (transport_params or {}).items():
|
|
280
|
+
# Convert complex values to compact repr
|
|
281
|
+
sv = repr(v)
|
|
282
|
+
sv = shorten(sv, width=80, placeholder="…")
|
|
283
|
+
tp_table.add_row(_escape(str(k)), _escape(sv))
|
|
284
|
+
|
|
285
|
+
# Last PDUs table (most recent last)
|
|
286
|
+
pdu_table = Table(
|
|
287
|
+
title="Last PDUs (most recent last)", title_style="bold", show_header=True, header_style="bold magenta"
|
|
288
|
+
)
|
|
289
|
+
for col in ("dir", "cat", "ctr", "ts", "len", "data"):
|
|
290
|
+
pdu_table.add_column(col, no_wrap=(col in {"dir", "cat", "ctr", "len"}), style="white")
|
|
291
|
+
for pdu in last_pdus:
|
|
292
|
+
try:
|
|
293
|
+
dir_ = str(pdu.get("dir", ""))
|
|
294
|
+
cat = str(pdu.get("cat", ""))
|
|
295
|
+
ctr = str(pdu.get("ctr", ""))
|
|
296
|
+
# Format timestamp: convert ns -> s with 5 decimals if numeric
|
|
297
|
+
ts_val = pdu.get("ts", "")
|
|
298
|
+
try:
|
|
299
|
+
ts_num = int(ts_val)
|
|
300
|
+
ts = f"{ts_num / 1_000_000_000:.5f}"
|
|
301
|
+
except Exception:
|
|
302
|
+
ts = str(ts_val)
|
|
303
|
+
ln = str(pdu.get("len", ""))
|
|
304
|
+
# Prefer showing actual data content; avoid repr quotes
|
|
305
|
+
data_val = pdu.get("data", "")
|
|
306
|
+
try:
|
|
307
|
+
if isinstance(data_val, (bytes, bytearray, list, tuple)):
|
|
308
|
+
# Lazily import to avoid hard dependency
|
|
309
|
+
from pyxcp.utils import hexDump as _hexDump
|
|
310
|
+
|
|
311
|
+
data_str = _hexDump(data_val)
|
|
312
|
+
else:
|
|
313
|
+
data_str = str(data_val)
|
|
314
|
+
except Exception:
|
|
315
|
+
data_str = str(data_val)
|
|
316
|
+
# Shorten potentially huge values to keep table compact
|
|
317
|
+
from textwrap import shorten as _shorten
|
|
318
|
+
|
|
319
|
+
ts = _shorten(ts, width=20, placeholder="…")
|
|
320
|
+
data = _shorten(data_str, width=40, placeholder="…")
|
|
321
|
+
# Escape strings to avoid Rich markup interpretation (e.g., '[' ']' in hex dumps)
|
|
322
|
+
dir_e = _escape(dir_)
|
|
323
|
+
cat_e = _escape(cat)
|
|
324
|
+
ctr_e = _escape(ctr)
|
|
325
|
+
ts_e = _escape(ts)
|
|
326
|
+
ln_e = _escape(ln)
|
|
327
|
+
data_e = _escape(data)
|
|
328
|
+
pdu_table.add_row(dir_e, cat_e, ctr_e, ts_e, ln_e, data_e)
|
|
329
|
+
except Exception:
|
|
330
|
+
# If anything odd in structure, add a single-cell row with repr
|
|
331
|
+
from textwrap import shorten as _shorten
|
|
332
|
+
|
|
333
|
+
pdu_table.add_row(_shorten(repr(pdu), width=80, placeholder="…"), "", "", "", "", "")
|
|
334
|
+
|
|
335
|
+
# Combine into a single panel and capture as text
|
|
336
|
+
console.print(Panel.fit(tp_table, title=header))
|
|
337
|
+
if last_pdus:
|
|
338
|
+
console.print(pdu_table)
|
|
339
|
+
rendered = console.export_text(clear=False)
|
|
340
|
+
|
|
341
|
+
except Exception:
|
|
342
|
+
# Rich not available or rendering failed; fallback to compact logger lines
|
|
343
|
+
self.logger.error(header)
|
|
344
|
+
if transport_params:
|
|
345
|
+
self.logger.error("transport_params: %s", transport_params)
|
|
346
|
+
if last_pdus:
|
|
347
|
+
self.logger.error("last_pdus (most recent last):")
|
|
348
|
+
for pdu in last_pdus:
|
|
349
|
+
try:
|
|
350
|
+
ts_val = pdu.get("ts", "")
|
|
351
|
+
try:
|
|
352
|
+
ts_num = int(ts_val)
|
|
353
|
+
ts_fmt = f"{ts_num / 1_000_000_000:.5f}"
|
|
354
|
+
except Exception:
|
|
355
|
+
ts_fmt = str(ts_val)
|
|
356
|
+
data_val = pdu.get("data", "")
|
|
357
|
+
if isinstance(data_val, (bytes, bytearray, list, tuple)):
|
|
358
|
+
from pyxcp.utils import hexDump as _hexDump
|
|
359
|
+
|
|
360
|
+
data_str = _hexDump(data_val)
|
|
361
|
+
else:
|
|
362
|
+
data_str = str(data_val)
|
|
363
|
+
pdu_copy = dict(pdu)
|
|
364
|
+
pdu_copy["ts"] = ts_fmt
|
|
365
|
+
pdu_copy["data"] = data_str
|
|
366
|
+
self.logger.error("%s", pdu_copy)
|
|
367
|
+
except Exception:
|
|
368
|
+
self.logger.error("%s", pdu)
|
|
369
|
+
except Exception:
|
|
370
|
+
# As a last resort, emit the whole diagnostics blob verbatim
|
|
371
|
+
try:
|
|
372
|
+
for line in diag.splitlines():
|
|
373
|
+
self.logger.error(line)
|
|
374
|
+
except Exception:
|
|
375
|
+
pass
|
|
376
|
+
return msg
|
|
247
377
|
|
|
248
378
|
def __str__(self):
|
|
249
379
|
return f"Handler(func = {func_name(self.func)} -- {self.arguments} service = {self.service} error_code = {self.error_code})"
|