caenhv-client-python 0.1.0__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kenji Shu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,41 @@
1
+ Metadata-Version: 2.4
2
+ Name: caenhv-client-python
3
+ Version: 0.1.0
4
+ Summary: Python interface to the caenhv-client GUI: fire/raise the application from other Python projects (e.g. labscript BLACS)
5
+ Author: Kenji Shu
6
+ License-Expression: MIT
7
+ Project-URL: Repository, https://github.com/kenji0923/caenhv-client
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.10
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Dynamic: license-file
14
+
15
+ # caenhv-client-python
16
+
17
+ Python interface to the [caenhv-client](https://github.com/kenji0923/caenhv-client)
18
+ GUI (CAEN HV control with channel linking). Zero dependencies; the GUI itself
19
+ is distributed as a standalone executable.
20
+
21
+ The only capability is *firing* the GUI — raise its window if running,
22
+ launch it otherwise — over a tiny local IPC protocol. There is deliberately
23
+ no remote control of HV settings.
24
+
25
+ ```python
26
+ from caenhv_client_python import fire_gui, notify_gui
27
+
28
+ fire_gui() # raise the window if running, otherwise launch the GUI
29
+ notify_gui() # raise only; returns False if the GUI is not running
30
+ ```
31
+
32
+ Configuration via environment variables:
33
+
34
+ - `CAENHV_CLIENT_COMMAND` — path (or command line) of the caenhv-client
35
+ executable, used when it is not on `PATH`.
36
+ - `CAENHV_CLIENT_IPC_NAME` — IPC server name (default `caenhv-client`),
37
+ must match the GUI's setting when overridden.
38
+
39
+ Works from any Python process, including labscript BLACS tabs and workers;
40
+ no Qt required (a Unix-socket / named-pipe transport is used, with PyQt5 as
41
+ an optional fallback).
@@ -0,0 +1,27 @@
1
+ # caenhv-client-python
2
+
3
+ Python interface to the [caenhv-client](https://github.com/kenji0923/caenhv-client)
4
+ GUI (CAEN HV control with channel linking). Zero dependencies; the GUI itself
5
+ is distributed as a standalone executable.
6
+
7
+ The only capability is *firing* the GUI — raise its window if running,
8
+ launch it otherwise — over a tiny local IPC protocol. There is deliberately
9
+ no remote control of HV settings.
10
+
11
+ ```python
12
+ from caenhv_client_python import fire_gui, notify_gui
13
+
14
+ fire_gui() # raise the window if running, otherwise launch the GUI
15
+ notify_gui() # raise only; returns False if the GUI is not running
16
+ ```
17
+
18
+ Configuration via environment variables:
19
+
20
+ - `CAENHV_CLIENT_COMMAND` — path (or command line) of the caenhv-client
21
+ executable, used when it is not on `PATH`.
22
+ - `CAENHV_CLIENT_IPC_NAME` — IPC server name (default `caenhv-client`),
23
+ must match the GUI's setting when overridden.
24
+
25
+ Works from any Python process, including labscript BLACS tabs and workers;
26
+ no Qt required (a Unix-socket / named-pipe transport is used, with PyQt5 as
27
+ an optional fallback).
@@ -0,0 +1,27 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "caenhv-client-python"
7
+ version = "0.1.0"
8
+ description = "Python interface to the caenhv-client GUI: fire/raise the application from other Python projects (e.g. labscript BLACS)"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Kenji Shu" }]
14
+ dependencies = []
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "Operating System :: OS Independent",
18
+ ]
19
+
20
+ [project.urls]
21
+ Repository = "https://github.com/kenji0923/caenhv-client"
22
+
23
+ [tool.setuptools]
24
+ package-dir = { "" = "src" }
25
+
26
+ [tool.setuptools.packages.find]
27
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,175 @@
1
+ """Python interface to the caenhv-client GUI application.
2
+
3
+ This package lets an external Python process (e.g. a labscript BLACS tab or
4
+ worker) *fire* the standalone caenhv-client GUI: raise the window if the app
5
+ is already running, or launch it otherwise. This is deliberately the only
6
+ remote capability — there is no remote control of HV settings.
7
+
8
+ The GUI listens on a QLocalServer (named pipe ``\\\\.\\pipe\\<name>`` on
9
+ Windows, Unix socket under the temp directory on POSIX). The protocol is a
10
+ single newline-terminated UTF-8 token: ``show`` (``raise`` is accepted as an
11
+ alias). Stdlib-only on every platform; PyQt5 is used as a fallback transport
12
+ only if it happens to be importable.
13
+
14
+ The GUI itself is distributed as a standalone executable (PyInstaller). Set
15
+ the ``CAENHV_CLIENT_COMMAND`` environment variable to its full path (or a
16
+ command line) if it is not on PATH.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import os
22
+ import shlex
23
+ import shutil
24
+ import socket
25
+ import subprocess
26
+ import sys
27
+ import tempfile
28
+ import time
29
+
30
+ DEFAULT_SERVER_NAME = "caenhv-client"
31
+ ENV_SERVER_NAME = "CAENHV_CLIENT_IPC_NAME"
32
+ ENV_LAUNCH_COMMAND = "CAENHV_CLIENT_COMMAND"
33
+ SHOW_COMMAND = b"show\n"
34
+
35
+ __all__ = [
36
+ "DEFAULT_SERVER_NAME",
37
+ "ENV_LAUNCH_COMMAND",
38
+ "ENV_SERVER_NAME",
39
+ "SHOW_COMMAND",
40
+ "default_launch_cmd",
41
+ "default_popen_kwargs",
42
+ "fire_gui",
43
+ "get_server_name",
44
+ "notify_gui",
45
+ ]
46
+
47
+ _qt_app = None
48
+
49
+
50
+ def get_server_name(server_name: str | None = None) -> str:
51
+ if server_name:
52
+ return server_name
53
+ return os.environ.get(ENV_SERVER_NAME) or DEFAULT_SERVER_NAME
54
+
55
+
56
+ def _notify_via_unix_socket(name: str, timeout: float) -> bool:
57
+ # QLocalServer places its Unix socket in the temp dir; both Qt and
58
+ # tempfile honor TMPDIR, so the paths agree.
59
+ path = os.path.join(tempfile.gettempdir(), name)
60
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
61
+ sock.settimeout(timeout)
62
+ try:
63
+ sock.connect(path)
64
+ sock.sendall(SHOW_COMMAND)
65
+ return True
66
+ except OSError:
67
+ return False
68
+ finally:
69
+ sock.close()
70
+
71
+
72
+ def _notify_via_windows_pipe(name: str) -> bool:
73
+ # QLocalServer pipes are file-openable; a plain write delivers the token.
74
+ try:
75
+ with open(rf"\\.\pipe\{name}", "wb", buffering=0) as pipe:
76
+ pipe.write(SHOW_COMMAND)
77
+ return True
78
+ except OSError:
79
+ return False
80
+
81
+
82
+ def _notify_via_qlocalsocket(name: str, timeout: float) -> bool:
83
+ from PyQt5 import QtCore, QtNetwork
84
+
85
+ global _qt_app
86
+ if QtCore.QCoreApplication.instance() is None:
87
+ _qt_app = QtCore.QCoreApplication([])
88
+ sock = QtNetwork.QLocalSocket()
89
+ sock.connectToServer(name)
90
+ try:
91
+ if not sock.waitForConnected(int(timeout * 1000)):
92
+ return False
93
+ sock.write(SHOW_COMMAND)
94
+ if not sock.waitForBytesWritten(int(timeout * 1000)):
95
+ return False
96
+ sock.disconnectFromServer()
97
+ return True
98
+ finally:
99
+ sock.abort()
100
+
101
+
102
+ def notify_gui(server_name: str | None = None, *, timeout: float = 1.0) -> bool:
103
+ """Deliver a show request to a running GUI. Return True if delivered."""
104
+ name = get_server_name(server_name)
105
+ if os.name == "posix":
106
+ if _notify_via_unix_socket(name, timeout):
107
+ return True
108
+ elif os.name == "nt":
109
+ if _notify_via_windows_pipe(name):
110
+ return True
111
+ try:
112
+ return _notify_via_qlocalsocket(name, timeout)
113
+ except ImportError:
114
+ return False
115
+
116
+
117
+ def default_launch_cmd() -> list[str] | None:
118
+ configured = os.environ.get(ENV_LAUNCH_COMMAND, "").strip()
119
+ if configured:
120
+ return shlex.split(configured, posix=(os.name != "nt"))
121
+ for script in ("caenhv-client-gui", "caenhv-client"):
122
+ found = shutil.which(script)
123
+ if found:
124
+ return [found]
125
+ try:
126
+ import caenhv_client # noqa: F401 (source/pip install present)
127
+ except ImportError:
128
+ return None
129
+ return [sys.executable, "-m", "caenhv_client"]
130
+
131
+
132
+ def default_popen_kwargs() -> dict:
133
+ kwargs: dict = {
134
+ "stdin": subprocess.DEVNULL,
135
+ "stdout": subprocess.DEVNULL,
136
+ "stderr": subprocess.DEVNULL,
137
+ "close_fds": True,
138
+ }
139
+ if os.name == "nt":
140
+ kwargs["creationflags"] = (
141
+ subprocess.DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP
142
+ )
143
+ else:
144
+ kwargs["start_new_session"] = True
145
+ return kwargs
146
+
147
+
148
+ def fire_gui(
149
+ server_name: str | None = None,
150
+ *,
151
+ launch_cmd: list[str] | None = None,
152
+ connect_timeout: float = 1.0,
153
+ launch_timeout: float = 15.0,
154
+ ) -> str:
155
+ """Raise the GUI if running, otherwise launch it detached.
156
+
157
+ Returns "raised" or "launched". Raises TimeoutError if a freshly
158
+ launched GUI does not start listening within launch_timeout, and
159
+ RuntimeError if no launch command can be determined.
160
+ """
161
+ if notify_gui(server_name, timeout=connect_timeout):
162
+ return "raised"
163
+ cmd = launch_cmd or default_launch_cmd()
164
+ if not cmd:
165
+ raise RuntimeError(
166
+ "caenhv-client GUI is not running and no launch command was found; "
167
+ f"set {ENV_LAUNCH_COMMAND} to the caenhv-client executable path"
168
+ )
169
+ subprocess.Popen(cmd, **default_popen_kwargs())
170
+ deadline = time.monotonic() + launch_timeout
171
+ while time.monotonic() < deadline:
172
+ if notify_gui(server_name, timeout=connect_timeout):
173
+ return "launched"
174
+ time.sleep(0.25)
175
+ raise TimeoutError(f"caenhv-client GUI did not start within {launch_timeout} s (cmd: {cmd})")
@@ -0,0 +1,41 @@
1
+ Metadata-Version: 2.4
2
+ Name: caenhv-client-python
3
+ Version: 0.1.0
4
+ Summary: Python interface to the caenhv-client GUI: fire/raise the application from other Python projects (e.g. labscript BLACS)
5
+ Author: Kenji Shu
6
+ License-Expression: MIT
7
+ Project-URL: Repository, https://github.com/kenji0923/caenhv-client
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.10
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Dynamic: license-file
14
+
15
+ # caenhv-client-python
16
+
17
+ Python interface to the [caenhv-client](https://github.com/kenji0923/caenhv-client)
18
+ GUI (CAEN HV control with channel linking). Zero dependencies; the GUI itself
19
+ is distributed as a standalone executable.
20
+
21
+ The only capability is *firing* the GUI — raise its window if running,
22
+ launch it otherwise — over a tiny local IPC protocol. There is deliberately
23
+ no remote control of HV settings.
24
+
25
+ ```python
26
+ from caenhv_client_python import fire_gui, notify_gui
27
+
28
+ fire_gui() # raise the window if running, otherwise launch the GUI
29
+ notify_gui() # raise only; returns False if the GUI is not running
30
+ ```
31
+
32
+ Configuration via environment variables:
33
+
34
+ - `CAENHV_CLIENT_COMMAND` — path (or command line) of the caenhv-client
35
+ executable, used when it is not on `PATH`.
36
+ - `CAENHV_CLIENT_IPC_NAME` — IPC server name (default `caenhv-client`),
37
+ must match the GUI's setting when overridden.
38
+
39
+ Works from any Python process, including labscript BLACS tabs and workers;
40
+ no Qt required (a Unix-socket / named-pipe transport is used, with PyQt5 as
41
+ an optional fallback).
@@ -0,0 +1,8 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/caenhv_client_python/__init__.py
5
+ src/caenhv_client_python.egg-info/PKG-INFO
6
+ src/caenhv_client_python.egg-info/SOURCES.txt
7
+ src/caenhv_client_python.egg-info/dependency_links.txt
8
+ src/caenhv_client_python.egg-info/top_level.txt