cpp-pd-code-simplify-interface 0.1.13__tar.gz → 0.1.15__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.
Files changed (14) hide show
  1. {cpp_pd_code_simplify_interface-0.1.13 → cpp_pd_code_simplify_interface-0.1.15}/PKG-INFO +4 -1
  2. {cpp_pd_code_simplify_interface-0.1.13 → cpp_pd_code_simplify_interface-0.1.15}/README.md +3 -0
  3. cpp_pd_code_simplify_interface-0.1.15/cpp_pd_code_simplify_interface/__main__.py +10 -0
  4. cpp_pd_code_simplify_interface-0.1.15/cpp_pd_code_simplify_interface/_worker.py +68 -0
  5. {cpp_pd_code_simplify_interface-0.1.13 → cpp_pd_code_simplify_interface-0.1.15}/cpp_pd_code_simplify_interface/data/include/pdcode_simplify/pdcode_simplify.hpp +1 -0
  6. {cpp_pd_code_simplify_interface-0.1.13 → cpp_pd_code_simplify_interface-0.1.15}/cpp_pd_code_simplify_interface/data/src/native_interface.cpp +9 -0
  7. {cpp_pd_code_simplify_interface-0.1.13 → cpp_pd_code_simplify_interface-0.1.15}/cpp_pd_code_simplify_interface/data/src/pdcode_simplify.cpp +7 -0
  8. {cpp_pd_code_simplify_interface-0.1.13 → cpp_pd_code_simplify_interface-0.1.15}/cpp_pd_code_simplify_interface/main.py +117 -76
  9. {cpp_pd_code_simplify_interface-0.1.13 → cpp_pd_code_simplify_interface-0.1.15}/pyproject.toml +1 -1
  10. cpp_pd_code_simplify_interface-0.1.13/cpp_pd_code_simplify_interface/__main__.py +0 -4
  11. cpp_pd_code_simplify_interface-0.1.13/cpp_pd_code_simplify_interface/_worker.py +0 -47
  12. {cpp_pd_code_simplify_interface-0.1.13 → cpp_pd_code_simplify_interface-0.1.15}/build_backend/cpp_pd_code_simplify_interface_build_backend.py +0 -0
  13. {cpp_pd_code_simplify_interface-0.1.13 → cpp_pd_code_simplify_interface-0.1.15}/cpp_pd_code_simplify_interface/__init__.py +0 -0
  14. {cpp_pd_code_simplify_interface-0.1.13 → cpp_pd_code_simplify_interface-0.1.15}/cpp_pd_code_simplify_interface/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cpp-pd-code-simplify-interface
3
- Version: 0.1.13
3
+ Version: 0.1.15
4
4
  Summary: Python interface for cpp-pd-code-simplify with runtime C++ compilation.
5
5
  License-Expression: MIT
6
6
  Author: GGN_2015
@@ -77,6 +77,9 @@ and sets `timed_out` to `True`. Verbose log lines use local wall-clock time in
77
77
  phase, verbose logs also include `actual_threads`, the worker count selected by
78
78
  the C++ backend for that phase. The backend call runs in a helper process, so
79
79
  `Ctrl+C` can terminate active C++ work and its worker threads cleanly.
80
+ Use `show_step_pd=True`, or CLI flag `--show-step-pd`, to print
81
+ `step_pd_code[ROUND]: PD[...]` to stdout after each mid-simplification witness
82
+ is applied and before that round's automatic R1/nugatory cleanup.
80
83
 
81
84
  Batch use:
82
85
 
@@ -57,6 +57,9 @@ and sets `timed_out` to `True`. Verbose log lines use local wall-clock time in
57
57
  phase, verbose logs also include `actual_threads`, the worker count selected by
58
58
  the C++ backend for that phase. The backend call runs in a helper process, so
59
59
  `Ctrl+C` can terminate active C++ work and its worker threads cleanly.
60
+ Use `show_step_pd=True`, or CLI flag `--show-step-pd`, to print
61
+ `step_pd_code[ROUND]: PD[...]` to stdout after each mid-simplification witness
62
+ is applied and before that round's automatic R1/nugatory cleanup.
60
63
 
61
64
  Batch use:
62
65
 
@@ -0,0 +1,10 @@
1
+ import json
2
+
3
+ from .main import main
4
+
5
+ if __name__ == "__main__":
6
+ try:
7
+ raise SystemExit(main())
8
+ except KeyboardInterrupt:
9
+ print(json.dumps({"error": "interrupted by Ctrl+C"}, indent=2))
10
+ raise SystemExit(130)
@@ -0,0 +1,68 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import sys
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from .main import _run_one_direct
9
+
10
+
11
+ def main() -> int:
12
+ try:
13
+ request: dict[str, Any] = json.loads(sys.stdin.read())
14
+ result = _run_one_direct(
15
+ str(request.get("pd_text", "")),
16
+ max_paths=int(request.get("max_paths", -1)),
17
+ ban_heuristic=bool(request.get("ban_heuristic", False)),
18
+ reduction_round=int(request.get("reduction_round", -1)),
19
+ max_thread=int(request.get("max_thread", -1)),
20
+ timeout=int(request.get("timeout", -1)),
21
+ verbose=bool(request.get("verbose", False)),
22
+ show_step_pd=bool(request.get("show_step_pd", False)),
23
+ known_crossingless_components=int(
24
+ request.get("known_crossingless_components", 0)
25
+ ),
26
+ remove_crossings=request.get("remove_crossings") or [],
27
+ )
28
+ output = json.dumps({"ok": True, "result": result}, separators=(",", ":"))
29
+ protocol_output_path = request.get("protocol_output_path")
30
+ if protocol_output_path:
31
+ Path(str(protocol_output_path)).write_text(output, encoding="utf-8")
32
+ else:
33
+ print(output)
34
+ return 0
35
+ except KeyboardInterrupt:
36
+ output = json.dumps(
37
+ {"ok": False, "error": "interrupted by Ctrl+C"},
38
+ separators=(",", ":"),
39
+ )
40
+ try:
41
+ request
42
+ except NameError:
43
+ request = {}
44
+ protocol_output_path = request.get("protocol_output_path")
45
+ if protocol_output_path:
46
+ Path(str(protocol_output_path)).write_text(output, encoding="utf-8")
47
+ else:
48
+ print(output)
49
+ return 130
50
+ except BaseException as exc: # noqa: BLE001 - return errors through JSON.
51
+ output = json.dumps(
52
+ {"ok": False, "error": str(exc)},
53
+ separators=(",", ":"),
54
+ )
55
+ try:
56
+ request
57
+ except NameError:
58
+ request = {}
59
+ protocol_output_path = request.get("protocol_output_path")
60
+ if protocol_output_path:
61
+ Path(str(protocol_output_path)).write_text(output, encoding="utf-8")
62
+ else:
63
+ print(output)
64
+ return 0
65
+
66
+
67
+ if __name__ == "__main__":
68
+ raise SystemExit(main())
@@ -59,6 +59,7 @@ struct SimplifierOptions {
59
59
  bool require_applicable = false;
60
60
  bool verbose = false;
61
61
  std::function<void(const std::string&)> progress;
62
+ std::function<void(int, const PDCode&)> step_pd_output;
62
63
  std::function<bool()> should_cancel;
63
64
  };
64
65
 
@@ -136,6 +136,7 @@ char* pdcode_simplify_run_json(
136
136
  int max_thread,
137
137
  int timeout_seconds,
138
138
  int verbose,
139
+ int show_step_pd,
139
140
  unsigned long long known_crossingless_components,
140
141
  const int* removed_crossings,
141
142
  unsigned long long removed_crossing_count) {
@@ -154,6 +155,14 @@ char* pdcode_simplify_run_json(
154
155
  options.progress = [](const std::string& message) {
155
156
  print_progress_log(message);
156
157
  };
158
+ if (show_step_pd != 0) {
159
+ options.step_pd_output = [](int round, const pdcode_simplify::PDCode& step_code) {
160
+ std::cout << "step_pd_code[" << round << "]: "
161
+ << pdcode_simplify::format_final_pd_code(step_code)
162
+ << '\n';
163
+ std::cout.flush();
164
+ };
165
+ }
157
166
 
158
167
  const pdcode_simplify::PDCode code = pdcode_simplify::parse_pd_code(text);
159
168
  std::size_t crossingless = static_cast<std::size_t>(known_crossingless_components);
@@ -1836,6 +1836,12 @@ void emit_progress(const SimplifierOptions& options, const std::string& message)
1836
1836
  }
1837
1837
  }
1838
1838
 
1839
+ void emit_step_pd(const SimplifierOptions& options, int round, const PDCode& code) {
1840
+ if (options.step_pd_output) {
1841
+ options.step_pd_output(round, code);
1842
+ }
1843
+ }
1844
+
1839
1845
  std::string search_mode_for_options(const SimplifierOptions& options) {
1840
1846
  if (options.max_paths == -1 && !options.ban_heuristic) {
1841
1847
  return "heuristic";
@@ -2639,6 +2645,7 @@ ReductionResult reduce_pd_code(
2639
2645
  const MidSimplificationApplyResult applied =
2640
2646
  apply_simplification_witness(output.code, search, output.crossingless_components);
2641
2647
  ++output.mid_simplification_rounds;
2648
+ emit_step_pd(run_options, round, applied.code);
2642
2649
  output.code = applied.code;
2643
2650
  output.crossingless_components = applied.crossingless_components;
2644
2651
  check_timeout(run_options);
@@ -15,6 +15,7 @@ import shutil
15
15
  import struct
16
16
  import subprocess
17
17
  import sys
18
+ import tempfile
18
19
  from importlib import resources
19
20
  from typing import Any, Optional, Sequence, Union
20
21
 
@@ -89,10 +90,7 @@ def normalize_pd_codes(pd_codes: PdManyInput) -> list[str]:
89
90
  return [normalize_pd_code(pd_code) for pd_code in pd_codes]
90
91
 
91
92
 
92
- def _label_for_block(text: str, block_start: int, label_prefix: str, index: int) -> str:
93
- line_start = text.rfind("\n", 0, block_start)
94
- line_start = 0 if line_start == -1 else line_start + 1
95
- before_block = text[line_start:block_start]
93
+ def _label_for_line_prefix(before_block: str, label_prefix: str, index: int) -> str:
96
94
  if ":" in before_block:
97
95
  line_label = before_block.split(":", 1)[0].strip()
98
96
  if line_label:
@@ -103,56 +101,62 @@ def _label_for_block(text: str, block_start: int, label_prefix: str, index: int)
103
101
 
104
102
 
105
103
  def _pd_file_jobs(path: str) -> list[tuple[str, str]]:
106
- text = pathlib.Path(path).read_text(encoding="utf-8")
107
104
  jobs: list[tuple[str, str]] = []
108
- position = 0
109
- index = 0
110
-
111
- while True:
112
- start = text.find("PD[", position)
113
- if start == -1:
114
- break
115
- depth = 0
116
- end = -1
117
- for cursor in range(start + 2, len(text)):
118
- if text[cursor] == "[":
119
- depth += 1
120
- elif text[cursor] == "]":
121
- depth -= 1
122
- if depth == 0:
123
- end = cursor
105
+ fallback_jobs: list[tuple[str, str]] = []
106
+ current_label: Optional[str] = None
107
+ current_block: list[str] = []
108
+ bracket_depth = 0
109
+
110
+ with pathlib.Path(path).open("r", encoding="utf-8") as input_file:
111
+ for line in input_file:
112
+ cleaned = line.strip()
113
+ if not jobs and not current_block and cleaned and not cleaned.startswith("#"):
114
+ label = path
115
+ payload = cleaned
116
+ if ":" in cleaned:
117
+ line_label, payload = cleaned.split(":", 1)
118
+ line_label = line_label.strip()
119
+ payload = payload.strip()
120
+ if line_label:
121
+ label = f"{path}:{line_label}"
122
+ elif fallback_jobs:
123
+ label = f"{path}#{len(fallback_jobs) + 1}"
124
+ fallback_jobs.append((label, payload))
125
+
126
+ cursor = 0
127
+ while cursor < len(line):
128
+ if current_block:
129
+ char = line[cursor]
130
+ current_block.append(char)
131
+ if char == "[":
132
+ bracket_depth += 1
133
+ elif char == "]":
134
+ bracket_depth -= 1
135
+ if bracket_depth == 0:
136
+ jobs.append((current_label or f"{path}#{len(jobs) + 1}", "".join(current_block)))
137
+ current_label = None
138
+ current_block = []
139
+ cursor += 1
140
+ continue
141
+
142
+ start = line.find("PD[", cursor)
143
+ if start == -1:
124
144
  break
125
- if end == -1:
126
- jobs.append((f"{path}#{index + 1}", text[start:].strip()))
127
- break
128
- jobs.append((
129
- _label_for_block(text, start, path, index),
130
- text[start : end + 1],
131
- ))
132
- index += 1
133
- position = end + 1
145
+ current_label = _label_for_line_prefix(line[:start], path, len(jobs))
146
+ current_block = ["PD["]
147
+ bracket_depth = 1
148
+ cursor = start + 3
134
149
 
135
150
  if jobs:
151
+ if current_block:
152
+ jobs.append((f"{path}#{len(jobs) + 1}", "".join(current_block).strip()))
136
153
  if len(jobs) == 1:
137
154
  jobs[0] = (path, jobs[0][1])
138
155
  return jobs
139
156
 
140
- for line in text.splitlines():
141
- cleaned = line.strip()
142
- if not cleaned or cleaned.startswith("#"):
143
- continue
144
- label = path
145
- payload = cleaned
146
- if ":" in cleaned:
147
- line_label, payload = cleaned.split(":", 1)
148
- line_label = line_label.strip()
149
- payload = payload.strip()
150
- if line_label:
151
- label = f"{path}:{line_label}"
152
- elif jobs:
153
- label = f"{path}#{len(jobs) + 1}"
154
- jobs.append((label, payload))
155
- return jobs
157
+ if current_block:
158
+ fallback_jobs.append((f"{path}#{len(fallback_jobs) + 1}", "".join(current_block).strip()))
159
+ return fallback_jobs
156
160
 
157
161
 
158
162
  @contextlib.contextmanager
@@ -763,6 +767,7 @@ def _load_library() -> ctypes.CDLL:
763
767
  ctypes.c_int,
764
768
  ctypes.c_int,
765
769
  ctypes.c_int,
770
+ ctypes.c_int,
766
771
  ctypes.c_ulonglong,
767
772
  ctypes.POINTER(ctypes.c_int),
768
773
  ctypes.c_ulonglong,
@@ -784,6 +789,7 @@ def _run_one_direct(
784
789
  max_thread: int = -1,
785
790
  timeout: int = -1,
786
791
  verbose: bool = False,
792
+ show_step_pd: bool = False,
787
793
  known_crossingless_components: int = 0,
788
794
  remove_crossings: Optional[Sequence[int]] = None,
789
795
  ) -> dict[str, Any]:
@@ -807,6 +813,7 @@ def _run_one_direct(
807
813
  int(max_thread),
808
814
  int(timeout),
809
815
  1 if verbose else 0,
816
+ 1 if show_step_pd else 0,
810
817
  int(known_crossingless_components),
811
818
  removed_array,
812
819
  int(removed_count),
@@ -847,6 +854,7 @@ def _run_one(
847
854
  max_thread: int = -1,
848
855
  timeout: int = -1,
849
856
  verbose: bool = False,
857
+ show_step_pd: bool = False,
850
858
  known_crossingless_components: int = 0,
851
859
  remove_crossings: Optional[Sequence[int]] = None,
852
860
  ) -> dict[str, Any]:
@@ -865,43 +873,62 @@ def _run_one(
865
873
  "max_thread": int(max_thread),
866
874
  "timeout": int(timeout),
867
875
  "verbose": bool(verbose),
876
+ "show_step_pd": bool(show_step_pd),
868
877
  "known_crossingless_components": int(known_crossingless_components),
869
878
  "remove_crossings": [int(value) for value in remove_crossings or []],
870
879
  }
880
+ protocol_output_path: Optional[pathlib.Path] = None
881
+ if show_step_pd:
882
+ fd, protocol_name = tempfile.mkstemp(
883
+ prefix="cpp-pd-code-simplify-interface-",
884
+ suffix=".json",
885
+ )
886
+ os.close(fd)
887
+ protocol_output_path = pathlib.Path(protocol_name)
888
+ request["protocol_output_path"] = str(protocol_output_path)
871
889
  env = os.environ.copy()
872
890
  project_root = str(pathlib.Path(__file__).resolve().parents[1])
873
891
  env["PYTHONPATH"] = project_root + os.pathsep + env.get("PYTHONPATH", "")
874
- proc = subprocess.Popen(
875
- [
876
- sys.executable,
877
- "-m",
878
- "cpp_pd_code_simplify_interface._worker",
879
- ],
880
- cwd=str(pathlib.Path.cwd()),
881
- text=True,
882
- stdin=subprocess.PIPE,
883
- stdout=subprocess.PIPE,
884
- stderr=None if verbose else subprocess.PIPE,
885
- env=env,
886
- )
887
892
  try:
888
- stdout, stderr = proc.communicate(json.dumps(request))
889
- except KeyboardInterrupt:
890
- _terminate_process(proc)
891
- raise
892
-
893
- if proc.returncode != 0:
894
- detail = (stderr or "").strip()
895
- raise PdCodeSimplifyInterfaceError(
896
- f"C++ interface worker failed with exit code {proc.returncode}"
897
- + (f": {detail}" if detail else "")
893
+ proc = subprocess.Popen(
894
+ [
895
+ sys.executable,
896
+ "-m",
897
+ "cpp_pd_code_simplify_interface._worker",
898
+ ],
899
+ cwd=str(pathlib.Path.cwd()),
900
+ text=True,
901
+ stdin=subprocess.PIPE,
902
+ stdout=None if show_step_pd else subprocess.PIPE,
903
+ stderr=None if verbose else subprocess.PIPE,
904
+ env=env,
898
905
  )
899
- try:
900
- envelope = json.loads(stdout)
901
- except json.JSONDecodeError as exc:
902
- raise PdCodeSimplifyInterfaceError(
903
- f"invalid interface worker JSON output: {stdout!r}"
904
- ) from exc
906
+ try:
907
+ stdout, stderr = proc.communicate(json.dumps(request))
908
+ except KeyboardInterrupt:
909
+ _terminate_process(proc)
910
+ raise
911
+
912
+ if proc.returncode != 0:
913
+ detail = (stderr or "").strip()
914
+ raise PdCodeSimplifyInterfaceError(
915
+ f"C++ interface worker failed with exit code {proc.returncode}"
916
+ + (f": {detail}" if detail else "")
917
+ )
918
+ if protocol_output_path is not None:
919
+ stdout = protocol_output_path.read_text(encoding="utf-8")
920
+ try:
921
+ envelope = json.loads(stdout or "")
922
+ except json.JSONDecodeError as exc:
923
+ raise PdCodeSimplifyInterfaceError(
924
+ f"invalid interface worker JSON output: {stdout!r}"
925
+ ) from exc
926
+ finally:
927
+ if protocol_output_path is not None:
928
+ try:
929
+ protocol_output_path.unlink()
930
+ except FileNotFoundError:
931
+ pass
905
932
  if not isinstance(envelope, dict):
906
933
  raise PdCodeSimplifyInterfaceError(
907
934
  f"invalid interface worker response type: {type(envelope)!r}"
@@ -923,6 +950,7 @@ def simplify(
923
950
  max_thread: int = -1,
924
951
  timeout: int = -1,
925
952
  verbose: bool = False,
953
+ show_step_pd: bool = False,
926
954
  known_crossingless_components: int = 0,
927
955
  remove_crossings: Optional[Sequence[int]] = None,
928
956
  ) -> dict[str, Any]:
@@ -936,6 +964,7 @@ def simplify(
936
964
  max_thread=max_thread,
937
965
  timeout=timeout,
938
966
  verbose=verbose,
967
+ show_step_pd=show_step_pd,
939
968
  known_crossingless_components=known_crossingless_components,
940
969
  remove_crossings=remove_crossings,
941
970
  )
@@ -950,6 +979,7 @@ def simplify_many(
950
979
  max_thread: int = -1,
951
980
  timeout: int = -1,
952
981
  verbose: bool = False,
982
+ show_step_pd: bool = False,
953
983
  known_crossingless_components: int = 0,
954
984
  remove_crossings: Optional[Sequence[int]] = None,
955
985
  ) -> list[dict[str, Any]]:
@@ -964,6 +994,7 @@ def simplify_many(
964
994
  max_thread=max_thread,
965
995
  timeout=timeout,
966
996
  verbose=verbose,
997
+ show_step_pd=show_step_pd,
967
998
  known_crossingless_components=known_crossingless_components,
968
999
  remove_crossings=remove_crossings,
969
1000
  )
@@ -982,6 +1013,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
982
1013
  parser.add_argument("--max-thread", type=int, default=-1)
983
1014
  parser.add_argument("--timeout", type=int, default=-1)
984
1015
  parser.add_argument("--verbose", action="store_true")
1016
+ parser.add_argument("--show-step-pd", action="store_true")
985
1017
  parser.add_argument("--known-crossingless-components", type=int, default=0)
986
1018
  parser.add_argument("--remove-crossings", help="comma-separated zero-based crossing indices")
987
1019
  args = parser.parse_args(argv)
@@ -1004,7 +1036,14 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
1004
1036
 
1005
1037
  exit_code = 0
1006
1038
  if args.pd_file:
1007
- jobs = _pd_file_jobs(args.pd_file)
1039
+ try:
1040
+ jobs = _pd_file_jobs(args.pd_file)
1041
+ except KeyboardInterrupt:
1042
+ print(json.dumps({"error": "interrupted by Ctrl+C"}, indent=2))
1043
+ return 130
1044
+ except Exception as exc:
1045
+ print(json.dumps({"error": str(exc)}, indent=2))
1046
+ return 2
1008
1047
  batch_payload = []
1009
1048
  show_labels = len(jobs) > 1
1010
1049
  for label, line in jobs:
@@ -1017,6 +1056,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
1017
1056
  max_thread=args.max_thread,
1018
1057
  timeout=args.timeout,
1019
1058
  verbose=args.verbose,
1059
+ show_step_pd=args.show_step_pd,
1020
1060
  known_crossingless_components=args.known_crossingless_components,
1021
1061
  remove_crossings=remove_crossings,
1022
1062
  )
@@ -1049,6 +1089,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
1049
1089
  max_thread=args.max_thread,
1050
1090
  timeout=args.timeout,
1051
1091
  verbose=args.verbose,
1092
+ show_step_pd=args.show_step_pd,
1052
1093
  known_crossingless_components=args.known_crossingless_components,
1053
1094
  remove_crossings=remove_crossings,
1054
1095
  )
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cpp-pd-code-simplify-interface"
3
- version = "0.1.13"
3
+ version = "0.1.15"
4
4
  description = "Python interface for cpp-pd-code-simplify with runtime C++ compilation."
5
5
  authors = [
6
6
  {name = "GGN_2015", email = "neko@jlulug.org"}
@@ -1,4 +0,0 @@
1
- from .main import main
2
-
3
- if __name__ == "__main__":
4
- raise SystemExit(main())
@@ -1,47 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- import sys
5
- from typing import Any
6
-
7
- from .main import _run_one_direct
8
-
9
-
10
- def main() -> int:
11
- try:
12
- request: dict[str, Any] = json.loads(sys.stdin.read())
13
- result = _run_one_direct(
14
- str(request.get("pd_text", "")),
15
- max_paths=int(request.get("max_paths", -1)),
16
- ban_heuristic=bool(request.get("ban_heuristic", False)),
17
- reduction_round=int(request.get("reduction_round", -1)),
18
- max_thread=int(request.get("max_thread", -1)),
19
- timeout=int(request.get("timeout", -1)),
20
- verbose=bool(request.get("verbose", False)),
21
- known_crossingless_components=int(
22
- request.get("known_crossingless_components", 0)
23
- ),
24
- remove_crossings=request.get("remove_crossings") or [],
25
- )
26
- print(json.dumps({"ok": True, "result": result}, separators=(",", ":")))
27
- return 0
28
- except KeyboardInterrupt:
29
- print(
30
- json.dumps(
31
- {"ok": False, "error": "interrupted by Ctrl+C"},
32
- separators=(",", ":"),
33
- )
34
- )
35
- return 130
36
- except BaseException as exc: # noqa: BLE001 - return errors through JSON.
37
- print(
38
- json.dumps(
39
- {"ok": False, "error": str(exc)},
40
- separators=(",", ":"),
41
- )
42
- )
43
- return 0
44
-
45
-
46
- if __name__ == "__main__":
47
- raise SystemExit(main())