scitex 2.16.2__py3-none-any.whl → 2.17.3__py3-none-any.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.
Files changed (70) hide show
  1. scitex/_dev/__init__.py +122 -0
  2. scitex/_dev/_config.py +391 -0
  3. scitex/_dev/_dashboard/__init__.py +11 -0
  4. scitex/_dev/_dashboard/_app.py +89 -0
  5. scitex/_dev/_dashboard/_routes.py +169 -0
  6. scitex/_dev/_dashboard/_scripts.py +301 -0
  7. scitex/_dev/_dashboard/_styles.py +205 -0
  8. scitex/_dev/_dashboard/_templates.py +117 -0
  9. scitex/_dev/_dashboard/static/version-dashboard-favicon.svg +12 -0
  10. scitex/_dev/_ecosystem.py +109 -0
  11. scitex/_dev/_github.py +360 -0
  12. scitex/_dev/_mcp/__init__.py +11 -0
  13. scitex/_dev/_mcp/handlers.py +182 -0
  14. scitex/_dev/_ssh.py +332 -0
  15. scitex/_dev/_versions.py +272 -0
  16. scitex/_mcp_resources/_cheatsheet.py +1 -1
  17. scitex/_mcp_resources/_modules.py +1 -1
  18. scitex/_mcp_tools/__init__.py +4 -0
  19. scitex/_mcp_tools/dev.py +186 -0
  20. scitex/_mcp_tools/verify.py +256 -0
  21. scitex/audio/_audio_check.py +84 -41
  22. scitex/cli/capture.py +45 -22
  23. scitex/cli/dev.py +494 -0
  24. scitex/cli/main.py +4 -0
  25. scitex/cli/stats.py +48 -20
  26. scitex/cli/verify.py +473 -0
  27. scitex/dev/plt/__init__.py +1 -1
  28. scitex/dev/plt/mpl/get_dir_ax.py +1 -1
  29. scitex/dev/plt/mpl/get_signatures.py +1 -1
  30. scitex/dev/plt/mpl/get_signatures_details.py +1 -1
  31. scitex/io/_load.py +8 -1
  32. scitex/io/_save.py +12 -0
  33. scitex/plt/__init__.py +16 -6
  34. scitex/session/README.md +2 -2
  35. scitex/session/__init__.py +1 -0
  36. scitex/session/_decorator.py +57 -33
  37. scitex/session/_lifecycle/__init__.py +23 -0
  38. scitex/session/_lifecycle/_close.py +225 -0
  39. scitex/session/_lifecycle/_config.py +112 -0
  40. scitex/session/_lifecycle/_matplotlib.py +83 -0
  41. scitex/session/_lifecycle/_start.py +246 -0
  42. scitex/session/_lifecycle/_utils.py +186 -0
  43. scitex/session/_manager.py +40 -3
  44. scitex/session/template.py +1 -1
  45. scitex/template/__init__.py +18 -1
  46. scitex/template/_templates/plt.py +1 -1
  47. scitex/template/_templates/session.py +1 -1
  48. scitex/template/clone_research_minimal.py +111 -0
  49. scitex/verify/README.md +300 -0
  50. scitex/verify/__init__.py +208 -0
  51. scitex/verify/_chain.py +369 -0
  52. scitex/verify/_db.py +600 -0
  53. scitex/verify/_hash.py +187 -0
  54. scitex/verify/_integration.py +127 -0
  55. scitex/verify/_rerun.py +253 -0
  56. scitex/verify/_tracker.py +330 -0
  57. scitex/verify/_visualize.py +44 -0
  58. scitex/verify/_viz/__init__.py +38 -0
  59. scitex/verify/_viz/_colors.py +84 -0
  60. scitex/verify/_viz/_format.py +302 -0
  61. scitex/verify/_viz/_json.py +192 -0
  62. scitex/verify/_viz/_mermaid.py +440 -0
  63. scitex/verify/_viz/_templates.py +246 -0
  64. scitex/verify/_viz/_utils.py +56 -0
  65. {scitex-2.16.2.dist-info → scitex-2.17.3.dist-info}/METADATA +2 -1
  66. {scitex-2.16.2.dist-info → scitex-2.17.3.dist-info}/RECORD +69 -28
  67. scitex/session/_lifecycle.py +0 -827
  68. {scitex-2.16.2.dist-info → scitex-2.17.3.dist-info}/WHEEL +0 -0
  69. {scitex-2.16.2.dist-info → scitex-2.17.3.dist-info}/entry_points.txt +0 -0
  70. {scitex-2.16.2.dist-info → scitex-2.17.3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,182 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2026-02-02
3
+ # File: scitex/_dev/_mcp/handlers.py
4
+
5
+ """MCP handler implementations for developer utilities."""
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any
10
+
11
+
12
+ async def list_versions_handler(
13
+ packages: list[str] | None = None,
14
+ ) -> dict[str, Any]:
15
+ """List versions across the scitex ecosystem.
16
+
17
+ Parameters
18
+ ----------
19
+ packages : list[str] | None
20
+ List of package names to check. If None, checks all ecosystem packages.
21
+
22
+ Returns
23
+ -------
24
+ dict
25
+ Version information for each package.
26
+ """
27
+ from scitex._dev import list_versions
28
+
29
+ return list_versions(packages)
30
+
31
+
32
+ async def check_versions_handler(
33
+ packages: list[str] | None = None,
34
+ ) -> dict[str, Any]:
35
+ """Check version consistency across the scitex ecosystem.
36
+
37
+ Parameters
38
+ ----------
39
+ packages : list[str] | None
40
+ List of package names to check. If None, checks all ecosystem packages.
41
+
42
+ Returns
43
+ -------
44
+ dict
45
+ Detailed version check results with summary.
46
+ """
47
+ from scitex._dev import check_versions
48
+
49
+ return check_versions(packages)
50
+
51
+
52
+ async def check_hosts_handler(
53
+ packages: list[str] | None = None,
54
+ hosts: list[str] | None = None,
55
+ ) -> dict[str, Any]:
56
+ """Check versions on SSH hosts.
57
+
58
+ Parameters
59
+ ----------
60
+ packages : list[str] | None
61
+ List of package names to check. If None, checks all ecosystem packages.
62
+ hosts : list[str] | None
63
+ List of host names to check. If None, checks all enabled hosts.
64
+
65
+ Returns
66
+ -------
67
+ dict
68
+ Host name -> package name -> version info mapping.
69
+ """
70
+ from scitex._dev import check_all_hosts
71
+
72
+ return check_all_hosts(packages=packages, hosts=hosts)
73
+
74
+
75
+ async def check_remotes_handler(
76
+ packages: list[str] | None = None,
77
+ remotes: list[str] | None = None,
78
+ ) -> dict[str, Any]:
79
+ """Check versions on GitHub remotes.
80
+
81
+ Parameters
82
+ ----------
83
+ packages : list[str] | None
84
+ List of package names to check. If None, checks all ecosystem packages.
85
+ remotes : list[str] | None
86
+ List of remote names to check. If None, checks all enabled remotes.
87
+
88
+ Returns
89
+ -------
90
+ dict
91
+ Remote name -> package name -> version info mapping.
92
+ """
93
+ from scitex._dev import check_all_remotes
94
+
95
+ return check_all_remotes(packages=packages, remotes=remotes)
96
+
97
+
98
+ async def get_config_handler() -> dict[str, Any]:
99
+ """Get current developer configuration.
100
+
101
+ Returns
102
+ -------
103
+ dict
104
+ Configuration including packages, hosts, remotes, branches.
105
+ """
106
+ from scitex._dev import get_config_path, load_config
107
+
108
+ config = load_config()
109
+ return {
110
+ "config_path": str(get_config_path()),
111
+ "packages": [
112
+ {
113
+ "name": p.name,
114
+ "local_path": p.local_path,
115
+ "pypi_name": p.pypi_name,
116
+ "github_repo": p.github_repo,
117
+ }
118
+ for p in config.packages
119
+ ],
120
+ "hosts": [
121
+ {
122
+ "name": h.name,
123
+ "hostname": h.hostname,
124
+ "user": h.user,
125
+ "role": h.role,
126
+ "enabled": h.enabled,
127
+ }
128
+ for h in config.hosts
129
+ ],
130
+ "github_remotes": [
131
+ {"name": r.name, "org": r.org, "enabled": r.enabled}
132
+ for r in config.github_remotes
133
+ ],
134
+ "branches": config.branches,
135
+ }
136
+
137
+
138
+ async def get_full_versions_handler(
139
+ packages: list[str] | None = None,
140
+ include_hosts: bool = False,
141
+ include_remotes: bool = False,
142
+ ) -> dict[str, Any]:
143
+ """Get comprehensive version data from all sources.
144
+
145
+ Parameters
146
+ ----------
147
+ packages : list[str] | None
148
+ List of package names to check. If None, checks all ecosystem packages.
149
+ include_hosts : bool
150
+ Include SSH host version checks.
151
+ include_remotes : bool
152
+ Include GitHub remote version checks.
153
+
154
+ Returns
155
+ -------
156
+ dict
157
+ Combined version data from local, hosts, and remotes.
158
+ """
159
+ from scitex._dev import check_all_hosts, check_all_remotes, list_versions
160
+
161
+ result = {
162
+ "packages": list_versions(packages),
163
+ "hosts": {},
164
+ "remotes": {},
165
+ }
166
+
167
+ if include_hosts:
168
+ try:
169
+ result["hosts"] = check_all_hosts(packages=packages)
170
+ except Exception as e:
171
+ result["hosts"] = {"error": str(e)}
172
+
173
+ if include_remotes:
174
+ try:
175
+ result["remotes"] = check_all_remotes(packages=packages)
176
+ except Exception as e:
177
+ result["remotes"] = {"error": str(e)}
178
+
179
+ return result
180
+
181
+
182
+ # EOF
scitex/_dev/_ssh.py ADDED
@@ -0,0 +1,332 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2026-02-02
3
+ # File: scitex/_dev/_ssh.py
4
+
5
+ """SSH-based remote version checking for scitex ecosystem."""
6
+
7
+ from __future__ import annotations
8
+
9
+ import subprocess
10
+ from typing import Any
11
+
12
+ from ._config import DevConfig, HostConfig, get_enabled_hosts, load_config
13
+
14
+
15
+ def get_remote_version(host: HostConfig, package: str) -> dict[str, Any]:
16
+ """Get version of a package on a remote host via SSH.
17
+
18
+ Parameters
19
+ ----------
20
+ host : HostConfig
21
+ Host configuration.
22
+ package : str
23
+ Package name (PyPI name).
24
+
25
+ Returns
26
+ -------
27
+ dict
28
+ Version info with keys: installed, status, error (if any).
29
+ """
30
+ # Build SSH command
31
+ ssh_args = ["ssh"]
32
+
33
+ if host.ssh_key:
34
+ ssh_args.extend(["-i", host.ssh_key])
35
+
36
+ if host.port != 22:
37
+ ssh_args.extend(["-p", str(host.port)])
38
+
39
+ # Add connection options for non-interactive use
40
+ ssh_args.extend(
41
+ [
42
+ "-o",
43
+ "BatchMode=yes",
44
+ "-o",
45
+ "StrictHostKeyChecking=accept-new",
46
+ "-o",
47
+ "ConnectTimeout=5",
48
+ ]
49
+ )
50
+
51
+ ssh_target = f"{host.user}@{host.hostname}"
52
+ ssh_args.append(ssh_target)
53
+
54
+ # Python command to get version
55
+ python_cmd = f"""python3 -c "
56
+ try:
57
+ from importlib.metadata import version
58
+ print(version('{package}'))
59
+ except Exception as e:
60
+ print('ERROR:' + str(e))
61
+ "
62
+ """
63
+ ssh_args.append(python_cmd)
64
+
65
+ try:
66
+ result = subprocess.run(
67
+ ssh_args,
68
+ capture_output=True,
69
+ text=True,
70
+ timeout=15,
71
+ )
72
+
73
+ output = result.stdout.strip()
74
+
75
+ if result.returncode != 0:
76
+ error = result.stderr.strip() or "SSH connection failed"
77
+ return {
78
+ "installed": None,
79
+ "status": "error",
80
+ "error": error,
81
+ }
82
+
83
+ if output.startswith("ERROR:"):
84
+ return {
85
+ "installed": None,
86
+ "status": "not_installed",
87
+ "error": output[6:],
88
+ }
89
+
90
+ return {
91
+ "installed": output,
92
+ "status": "ok",
93
+ }
94
+
95
+ except subprocess.TimeoutExpired:
96
+ return {
97
+ "installed": None,
98
+ "status": "timeout",
99
+ "error": "SSH connection timed out",
100
+ }
101
+ except Exception as e:
102
+ return {
103
+ "installed": None,
104
+ "status": "error",
105
+ "error": str(e),
106
+ }
107
+
108
+
109
+ def get_remote_versions(
110
+ host: HostConfig,
111
+ packages: list[str],
112
+ ) -> dict[str, dict[str, Any]]:
113
+ """Get versions of multiple packages on a remote host.
114
+
115
+ Parameters
116
+ ----------
117
+ host : HostConfig
118
+ Host configuration.
119
+ packages : list[str]
120
+ List of package names.
121
+
122
+ Returns
123
+ -------
124
+ dict
125
+ Package name -> version info mapping.
126
+ """
127
+ # Build SSH command that checks all packages at once
128
+ ssh_args = ["ssh"]
129
+
130
+ if host.ssh_key:
131
+ ssh_args.extend(["-i", host.ssh_key])
132
+
133
+ if host.port != 22:
134
+ ssh_args.extend(["-p", str(host.port)])
135
+
136
+ ssh_args.extend(
137
+ [
138
+ "-o",
139
+ "BatchMode=yes",
140
+ "-o",
141
+ "StrictHostKeyChecking=accept-new",
142
+ "-o",
143
+ "ConnectTimeout=5",
144
+ ]
145
+ )
146
+
147
+ ssh_target = f"{host.user}@{host.hostname}"
148
+ ssh_args.append(ssh_target)
149
+
150
+ # Build Python command to check all packages
151
+ packages_str = ",".join(f"'{p}'" for p in packages)
152
+ python_cmd = f"""python3 -c "
153
+ import json
154
+ from importlib.metadata import version
155
+ results = {{}}
156
+ for pkg in [{packages_str}]:
157
+ try:
158
+ results[pkg] = {{'installed': version(pkg), 'status': 'ok'}}
159
+ except Exception as e:
160
+ results[pkg] = {{'installed': None, 'status': 'not_installed', 'error': str(e)}}
161
+ print(json.dumps(results))
162
+ "
163
+ """
164
+ ssh_args.append(python_cmd)
165
+
166
+ try:
167
+ result = subprocess.run(
168
+ ssh_args,
169
+ capture_output=True,
170
+ text=True,
171
+ timeout=30,
172
+ )
173
+
174
+ if result.returncode != 0:
175
+ error = result.stderr.strip() or "SSH connection failed"
176
+ return {
177
+ pkg: {"installed": None, "status": "error", "error": error}
178
+ for pkg in packages
179
+ }
180
+
181
+ import json
182
+
183
+ try:
184
+ return json.loads(result.stdout.strip())
185
+ except json.JSONDecodeError:
186
+ return {
187
+ pkg: {
188
+ "installed": None,
189
+ "status": "error",
190
+ "error": f"Invalid response: {result.stdout[:100]}",
191
+ }
192
+ for pkg in packages
193
+ }
194
+
195
+ except subprocess.TimeoutExpired:
196
+ return {
197
+ pkg: {"installed": None, "status": "timeout", "error": "SSH timed out"}
198
+ for pkg in packages
199
+ }
200
+ except Exception as e:
201
+ return {
202
+ pkg: {"installed": None, "status": "error", "error": str(e)}
203
+ for pkg in packages
204
+ }
205
+
206
+
207
+ def check_all_hosts(
208
+ packages: list[str] | None = None,
209
+ hosts: list[str] | None = None,
210
+ config: DevConfig | None = None,
211
+ ) -> dict[str, dict[str, dict[str, Any]]]:
212
+ """Check versions on all enabled hosts.
213
+
214
+ Parameters
215
+ ----------
216
+ packages : list[str] | None
217
+ List of package names. If None, uses ecosystem packages.
218
+ hosts : list[str] | None
219
+ List of host names to check. If None, checks all enabled hosts.
220
+ config : DevConfig | None
221
+ Configuration to use. If None, loads default config.
222
+
223
+ Returns
224
+ -------
225
+ dict
226
+ Mapping: host_name -> package_name -> version_info
227
+ """
228
+ if config is None:
229
+ config = load_config()
230
+
231
+ if packages is None:
232
+ from ._ecosystem import get_all_packages
233
+
234
+ packages = get_all_packages()
235
+
236
+ # Get pypi names for packages
237
+ from ._ecosystem import ECOSYSTEM
238
+
239
+ pypi_names = []
240
+ name_map = {} # pypi_name -> package_name
241
+ for pkg in packages:
242
+ if pkg in ECOSYSTEM:
243
+ pypi_name = ECOSYSTEM[pkg].get("pypi_name", pkg)
244
+ else:
245
+ pypi_name = pkg
246
+ pypi_names.append(pypi_name)
247
+ name_map[pypi_name] = pkg
248
+
249
+ # Get enabled hosts
250
+ enabled_hosts = get_enabled_hosts(config)
251
+ if hosts:
252
+ enabled_hosts = [h for h in enabled_hosts if h.name in hosts]
253
+
254
+ results: dict[str, dict[str, dict[str, Any]]] = {}
255
+
256
+ for host in enabled_hosts:
257
+ host_versions = get_remote_versions(host, pypi_names)
258
+ # Map back to package names
259
+ results[host.name] = {
260
+ name_map.get(pypi, pypi): info for pypi, info in host_versions.items()
261
+ }
262
+ # Add host metadata
263
+ results[host.name]["_host"] = {
264
+ "hostname": host.hostname,
265
+ "role": host.role,
266
+ "user": host.user,
267
+ }
268
+
269
+ return results
270
+
271
+
272
+ def test_host_connection(host: HostConfig) -> dict[str, Any]:
273
+ """Test SSH connection to a host.
274
+
275
+ Parameters
276
+ ----------
277
+ host : HostConfig
278
+ Host to test.
279
+
280
+ Returns
281
+ -------
282
+ dict
283
+ Connection status with keys: connected, error, python_version.
284
+ """
285
+ ssh_args = ["ssh"]
286
+
287
+ if host.ssh_key:
288
+ ssh_args.extend(["-i", host.ssh_key])
289
+
290
+ if host.port != 22:
291
+ ssh_args.extend(["-p", str(host.port)])
292
+
293
+ ssh_args.extend(
294
+ [
295
+ "-o",
296
+ "BatchMode=yes",
297
+ "-o",
298
+ "StrictHostKeyChecking=accept-new",
299
+ "-o",
300
+ "ConnectTimeout=5",
301
+ ]
302
+ )
303
+
304
+ ssh_target = f"{host.user}@{host.hostname}"
305
+ ssh_args.append(ssh_target)
306
+ ssh_args.append("python3 --version")
307
+
308
+ try:
309
+ result = subprocess.run(
310
+ ssh_args,
311
+ capture_output=True,
312
+ text=True,
313
+ timeout=10,
314
+ )
315
+
316
+ if result.returncode == 0:
317
+ return {
318
+ "connected": True,
319
+ "python_version": result.stdout.strip(),
320
+ }
321
+ return {
322
+ "connected": False,
323
+ "error": result.stderr.strip() or "Connection failed",
324
+ }
325
+
326
+ except subprocess.TimeoutExpired:
327
+ return {"connected": False, "error": "Connection timed out"}
328
+ except Exception as e:
329
+ return {"connected": False, "error": str(e)}
330
+
331
+
332
+ # EOF