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.
- scitex/_dev/__init__.py +122 -0
- scitex/_dev/_config.py +391 -0
- scitex/_dev/_dashboard/__init__.py +11 -0
- scitex/_dev/_dashboard/_app.py +89 -0
- scitex/_dev/_dashboard/_routes.py +169 -0
- scitex/_dev/_dashboard/_scripts.py +301 -0
- scitex/_dev/_dashboard/_styles.py +205 -0
- scitex/_dev/_dashboard/_templates.py +117 -0
- scitex/_dev/_dashboard/static/version-dashboard-favicon.svg +12 -0
- scitex/_dev/_ecosystem.py +109 -0
- scitex/_dev/_github.py +360 -0
- scitex/_dev/_mcp/__init__.py +11 -0
- scitex/_dev/_mcp/handlers.py +182 -0
- scitex/_dev/_ssh.py +332 -0
- scitex/_dev/_versions.py +272 -0
- scitex/_mcp_resources/_cheatsheet.py +1 -1
- scitex/_mcp_resources/_modules.py +1 -1
- scitex/_mcp_tools/__init__.py +4 -0
- scitex/_mcp_tools/dev.py +186 -0
- scitex/_mcp_tools/verify.py +256 -0
- scitex/audio/_audio_check.py +84 -41
- scitex/cli/capture.py +45 -22
- scitex/cli/dev.py +494 -0
- scitex/cli/main.py +4 -0
- scitex/cli/stats.py +48 -20
- scitex/cli/verify.py +473 -0
- scitex/dev/plt/__init__.py +1 -1
- scitex/dev/plt/mpl/get_dir_ax.py +1 -1
- scitex/dev/plt/mpl/get_signatures.py +1 -1
- scitex/dev/plt/mpl/get_signatures_details.py +1 -1
- scitex/io/_load.py +8 -1
- scitex/io/_save.py +12 -0
- scitex/plt/__init__.py +16 -6
- scitex/session/README.md +2 -2
- scitex/session/__init__.py +1 -0
- scitex/session/_decorator.py +57 -33
- scitex/session/_lifecycle/__init__.py +23 -0
- scitex/session/_lifecycle/_close.py +225 -0
- scitex/session/_lifecycle/_config.py +112 -0
- scitex/session/_lifecycle/_matplotlib.py +83 -0
- scitex/session/_lifecycle/_start.py +246 -0
- scitex/session/_lifecycle/_utils.py +186 -0
- scitex/session/_manager.py +40 -3
- scitex/session/template.py +1 -1
- scitex/template/__init__.py +18 -1
- scitex/template/_templates/plt.py +1 -1
- scitex/template/_templates/session.py +1 -1
- scitex/template/clone_research_minimal.py +111 -0
- scitex/verify/README.md +300 -0
- scitex/verify/__init__.py +208 -0
- scitex/verify/_chain.py +369 -0
- scitex/verify/_db.py +600 -0
- scitex/verify/_hash.py +187 -0
- scitex/verify/_integration.py +127 -0
- scitex/verify/_rerun.py +253 -0
- scitex/verify/_tracker.py +330 -0
- scitex/verify/_visualize.py +44 -0
- scitex/verify/_viz/__init__.py +38 -0
- scitex/verify/_viz/_colors.py +84 -0
- scitex/verify/_viz/_format.py +302 -0
- scitex/verify/_viz/_json.py +192 -0
- scitex/verify/_viz/_mermaid.py +440 -0
- scitex/verify/_viz/_templates.py +246 -0
- scitex/verify/_viz/_utils.py +56 -0
- {scitex-2.16.2.dist-info → scitex-2.17.3.dist-info}/METADATA +2 -1
- {scitex-2.16.2.dist-info → scitex-2.17.3.dist-info}/RECORD +69 -28
- scitex/session/_lifecycle.py +0 -827
- {scitex-2.16.2.dist-info → scitex-2.17.3.dist-info}/WHEEL +0 -0
- {scitex-2.16.2.dist-info → scitex-2.17.3.dist-info}/entry_points.txt +0 -0
- {scitex-2.16.2.dist-info → scitex-2.17.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: "2026-02-01 (ywatanabe)"
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-python/src/scitex/session/_lifecycle/_start.py
|
|
4
|
+
"""Session start function."""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import inspect
|
|
9
|
+
import logging
|
|
10
|
+
import os as _os
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any, Dict, Optional, Tuple, Union
|
|
13
|
+
|
|
14
|
+
from scitex.dict import DotDict
|
|
15
|
+
from scitex.logging import getLogger
|
|
16
|
+
from scitex.repro import RandomStateManager
|
|
17
|
+
from scitex.str._clean_path import clean_path
|
|
18
|
+
|
|
19
|
+
from .._manager import get_global_session_manager
|
|
20
|
+
from ._config import setup_configs
|
|
21
|
+
from ._matplotlib import setup_matplotlib
|
|
22
|
+
from ._utils import (
|
|
23
|
+
clear_python_log_dir,
|
|
24
|
+
get_debug_mode,
|
|
25
|
+
initialize_env,
|
|
26
|
+
print_header,
|
|
27
|
+
simplify_relative_path,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
logger = getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
# For development code flow analysis
|
|
33
|
+
try:
|
|
34
|
+
from scitex.dev._analyze_code_flow import analyze_code_flow
|
|
35
|
+
except ImportError:
|
|
36
|
+
|
|
37
|
+
def analyze_code_flow(file):
|
|
38
|
+
return "Code flow analysis not available"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def start(
|
|
42
|
+
sys=None,
|
|
43
|
+
plt=None,
|
|
44
|
+
file: Optional[str] = None,
|
|
45
|
+
sdir: Optional[Union[str, Path]] = None,
|
|
46
|
+
sdir_suffix: Optional[str] = None,
|
|
47
|
+
args: Optional[Any] = None,
|
|
48
|
+
os: Optional[Any] = None,
|
|
49
|
+
random: Optional[Any] = None,
|
|
50
|
+
np: Optional[Any] = None,
|
|
51
|
+
torch: Optional[Any] = None,
|
|
52
|
+
seed: int = 42,
|
|
53
|
+
agg: bool = False,
|
|
54
|
+
fig_size_mm: Tuple[int, int] = (160, 100),
|
|
55
|
+
fig_scale: float = 1.0,
|
|
56
|
+
dpi_display: int = 100,
|
|
57
|
+
dpi_save: int = 300,
|
|
58
|
+
fontsize="small",
|
|
59
|
+
autolayout=True,
|
|
60
|
+
show_execution_flow=False,
|
|
61
|
+
hide_top_right_spines: bool = True,
|
|
62
|
+
alpha: float = 0.9,
|
|
63
|
+
line_width: float = 1.0,
|
|
64
|
+
clear_logs: bool = False,
|
|
65
|
+
verbose: bool = True,
|
|
66
|
+
) -> Tuple[DotDict, Any, Any, Any, Optional[Dict[str, Any]], Any]:
|
|
67
|
+
"""Initialize experiment session with reproducibility settings.
|
|
68
|
+
|
|
69
|
+
Parameters
|
|
70
|
+
----------
|
|
71
|
+
sys : module, optional
|
|
72
|
+
Python sys module for I/O redirection
|
|
73
|
+
plt : module, optional
|
|
74
|
+
Matplotlib pyplot module for plotting configuration
|
|
75
|
+
file : str, optional
|
|
76
|
+
Script file path. If None, automatically detected
|
|
77
|
+
sdir : Union[str, Path], optional
|
|
78
|
+
Save directory path
|
|
79
|
+
sdir_suffix : str, optional
|
|
80
|
+
Suffix to append to save directory
|
|
81
|
+
args : object, optional
|
|
82
|
+
Command line arguments or configuration object
|
|
83
|
+
seed : int, default=42
|
|
84
|
+
Random seed for reproducibility
|
|
85
|
+
agg : bool, default=False
|
|
86
|
+
Whether to use matplotlib Agg backend
|
|
87
|
+
verbose : bool, default=True
|
|
88
|
+
Whether to print detailed information
|
|
89
|
+
|
|
90
|
+
Returns
|
|
91
|
+
-------
|
|
92
|
+
tuple
|
|
93
|
+
(CONFIGS, stdout, stderr, plt, COLORS, rng)
|
|
94
|
+
"""
|
|
95
|
+
IS_DEBUG = get_debug_mode()
|
|
96
|
+
ID, PID = initialize_env(IS_DEBUG)
|
|
97
|
+
|
|
98
|
+
# Convert Path objects to strings for internal processing
|
|
99
|
+
if sdir is not None and isinstance(sdir, Path):
|
|
100
|
+
sdir = str(sdir)
|
|
101
|
+
|
|
102
|
+
# Defines SDIR
|
|
103
|
+
if sdir is None:
|
|
104
|
+
# Define __file__
|
|
105
|
+
if file:
|
|
106
|
+
caller_file = file
|
|
107
|
+
else:
|
|
108
|
+
caller_file = inspect.stack()[1].filename
|
|
109
|
+
if "ipython" in caller_file:
|
|
110
|
+
caller_file = f"/tmp/{_os.getenv('USER')}.py"
|
|
111
|
+
|
|
112
|
+
# Convert to absolute path if relative and resolve symlinks
|
|
113
|
+
if not _os.path.isabs(caller_file):
|
|
114
|
+
caller_file = _os.path.realpath(_os.path.abspath(caller_file))
|
|
115
|
+
else:
|
|
116
|
+
caller_file = _os.path.realpath(caller_file)
|
|
117
|
+
|
|
118
|
+
# Define sdir
|
|
119
|
+
sdir = clean_path(_os.path.splitext(caller_file)[0] + f"_out/RUNNING/{ID}/")
|
|
120
|
+
|
|
121
|
+
# Optional
|
|
122
|
+
if sdir_suffix:
|
|
123
|
+
sdir = sdir[:-1] + f"-{sdir_suffix}/"
|
|
124
|
+
else:
|
|
125
|
+
caller_file = file
|
|
126
|
+
|
|
127
|
+
if clear_logs and caller_file:
|
|
128
|
+
clear_python_log_dir(sdir + caller_file + "/")
|
|
129
|
+
_os.makedirs(sdir, exist_ok=True)
|
|
130
|
+
relative_sdir = simplify_relative_path(sdir)
|
|
131
|
+
|
|
132
|
+
# Setup configs - use caller_file (computed) instead of file (parameter)
|
|
133
|
+
CONFIGS = setup_configs(
|
|
134
|
+
IS_DEBUG, ID, PID, caller_file, sdir, relative_sdir, verbose
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# Logging
|
|
138
|
+
if sys is not None:
|
|
139
|
+
from scitex.io._flush import flush
|
|
140
|
+
from scitex.logging._Tee import tee
|
|
141
|
+
|
|
142
|
+
flush(sys)
|
|
143
|
+
sys.stdout, sys.stderr = tee(sys, sdir=sdir, verbose=verbose)
|
|
144
|
+
CONFIGS["_sys"] = sys
|
|
145
|
+
|
|
146
|
+
# Redirect logging handlers to use the tee-wrapped streams
|
|
147
|
+
_redirect_logging_handlers(sys)
|
|
148
|
+
|
|
149
|
+
# Initialize RandomStateManager
|
|
150
|
+
rng = RandomStateManager(seed=seed, verbose=verbose)
|
|
151
|
+
if verbose:
|
|
152
|
+
logger.info(f"Initialized RandomStateManager with seed {seed}")
|
|
153
|
+
|
|
154
|
+
# Matplotlib configurations
|
|
155
|
+
plt, COLORS = setup_matplotlib(
|
|
156
|
+
plt,
|
|
157
|
+
agg,
|
|
158
|
+
fig_size_mm=fig_size_mm,
|
|
159
|
+
fig_scale=fig_scale,
|
|
160
|
+
dpi_display=dpi_display,
|
|
161
|
+
dpi_save=dpi_save,
|
|
162
|
+
hide_top_right_spines=hide_top_right_spines,
|
|
163
|
+
alpha=alpha,
|
|
164
|
+
line_width=line_width,
|
|
165
|
+
fontsize=fontsize,
|
|
166
|
+
autolayout=autolayout,
|
|
167
|
+
verbose=verbose,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Adds argument-parsed variables
|
|
171
|
+
if args is not None:
|
|
172
|
+
CONFIGS["ARGS"] = vars(args) if hasattr(args, "__dict__") else args
|
|
173
|
+
|
|
174
|
+
CONFIGS = DotDict(CONFIGS)
|
|
175
|
+
|
|
176
|
+
# Register session
|
|
177
|
+
session_manager = get_global_session_manager()
|
|
178
|
+
session_manager.create_session(ID, CONFIGS)
|
|
179
|
+
|
|
180
|
+
print_header(ID, PID, caller_file, args, CONFIGS, verbose)
|
|
181
|
+
|
|
182
|
+
if show_execution_flow:
|
|
183
|
+
from scitex.str._printc import printc as _printc
|
|
184
|
+
|
|
185
|
+
structure = analyze_code_flow(caller_file)
|
|
186
|
+
_printc(structure)
|
|
187
|
+
|
|
188
|
+
# Start verification tracking
|
|
189
|
+
_start_verification(CONFIGS)
|
|
190
|
+
|
|
191
|
+
# Return appropriate values based on whether sys was provided
|
|
192
|
+
if sys is not None:
|
|
193
|
+
return CONFIGS, sys.stdout, sys.stderr, plt, COLORS, rng
|
|
194
|
+
else:
|
|
195
|
+
return CONFIGS, None, None, plt, COLORS, rng
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _redirect_logging_handlers(sys) -> None:
|
|
199
|
+
"""Redirect logging handlers to use tee-wrapped streams."""
|
|
200
|
+
# Update all existing StreamHandler instances
|
|
201
|
+
for logger_name in list(logging.Logger.manager.loggerDict.keys()):
|
|
202
|
+
try:
|
|
203
|
+
lgr = logging.getLogger(logger_name)
|
|
204
|
+
for handler in lgr.handlers:
|
|
205
|
+
if isinstance(handler, logging.StreamHandler):
|
|
206
|
+
if not hasattr(handler, "stream"):
|
|
207
|
+
continue
|
|
208
|
+
if handler.stream in (sys.__stderr__, sys.__stdout__):
|
|
209
|
+
handler.stream = (
|
|
210
|
+
sys.stderr
|
|
211
|
+
if handler.stream == sys.__stderr__
|
|
212
|
+
else sys.stdout
|
|
213
|
+
)
|
|
214
|
+
except Exception:
|
|
215
|
+
pass
|
|
216
|
+
|
|
217
|
+
# Also update the root logger handlers
|
|
218
|
+
try:
|
|
219
|
+
root_logger = logging.getLogger()
|
|
220
|
+
for handler in root_logger.handlers:
|
|
221
|
+
if isinstance(handler, logging.StreamHandler):
|
|
222
|
+
if not hasattr(handler, "stream"):
|
|
223
|
+
continue
|
|
224
|
+
if handler.stream in (sys.__stderr__, sys.__stdout__):
|
|
225
|
+
handler.stream = (
|
|
226
|
+
sys.stderr if handler.stream == sys.__stderr__ else sys.stdout
|
|
227
|
+
)
|
|
228
|
+
except Exception:
|
|
229
|
+
pass
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _start_verification(CONFIG) -> None:
|
|
233
|
+
"""Start verification tracking for this session."""
|
|
234
|
+
try:
|
|
235
|
+
from scitex.verify import on_session_start
|
|
236
|
+
|
|
237
|
+
session_id = CONFIG.get("ID", "unknown")
|
|
238
|
+
file_path = CONFIG.get("FILE")
|
|
239
|
+
if file_path is not None:
|
|
240
|
+
file_path = str(file_path)
|
|
241
|
+
on_session_start(session_id=session_id, script_path=file_path)
|
|
242
|
+
except Exception:
|
|
243
|
+
pass
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
# EOF
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: "2026-02-01 (ywatanabe)"
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-python/src/scitex/session/_lifecycle/_utils.py
|
|
4
|
+
"""Utility functions for session lifecycle."""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import os as _os
|
|
9
|
+
import re
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from time import sleep
|
|
12
|
+
from typing import Any, Dict, Tuple
|
|
13
|
+
|
|
14
|
+
from scitex.logging import getLogger
|
|
15
|
+
from scitex.repro._gen_ID import gen_ID
|
|
16
|
+
from scitex.str._printc import printc as _printc
|
|
17
|
+
|
|
18
|
+
logger = getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_scitex_version() -> str:
|
|
22
|
+
"""Gets scitex version."""
|
|
23
|
+
try:
|
|
24
|
+
import scitex
|
|
25
|
+
|
|
26
|
+
return scitex.__version__
|
|
27
|
+
except Exception as e:
|
|
28
|
+
print(e)
|
|
29
|
+
return "(not found)"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_debug_mode() -> bool:
|
|
33
|
+
"""Get debug mode from configuration."""
|
|
34
|
+
try:
|
|
35
|
+
from scitex.io._load import load
|
|
36
|
+
|
|
37
|
+
IS_DEBUG_PATH = "./config/IS_DEBUG.yaml"
|
|
38
|
+
if _os.path.exists(IS_DEBUG_PATH):
|
|
39
|
+
IS_DEBUG = load(IS_DEBUG_PATH).get("IS_DEBUG", False)
|
|
40
|
+
if IS_DEBUG == "true":
|
|
41
|
+
IS_DEBUG = True
|
|
42
|
+
else:
|
|
43
|
+
IS_DEBUG = False
|
|
44
|
+
|
|
45
|
+
except Exception as e:
|
|
46
|
+
print(e)
|
|
47
|
+
IS_DEBUG = False
|
|
48
|
+
return IS_DEBUG
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def initialize_env(IS_DEBUG: bool) -> Tuple[str, int]:
|
|
52
|
+
"""Initialize environment with ID and PID.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
IS_DEBUG : bool
|
|
57
|
+
Debug mode flag
|
|
58
|
+
|
|
59
|
+
Returns
|
|
60
|
+
-------
|
|
61
|
+
tuple
|
|
62
|
+
(ID, PID) - Unique identifier and Process ID
|
|
63
|
+
"""
|
|
64
|
+
ID = gen_ID(N=4) if not IS_DEBUG else "DEBUG_" + gen_ID(N=4)
|
|
65
|
+
PID = _os.getpid()
|
|
66
|
+
return ID, PID
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def simplify_relative_path(sdir: str) -> str:
|
|
70
|
+
"""Simplify the relative path by removing specific patterns.
|
|
71
|
+
|
|
72
|
+
Parameters
|
|
73
|
+
----------
|
|
74
|
+
sdir : str
|
|
75
|
+
The directory path to simplify
|
|
76
|
+
|
|
77
|
+
Returns
|
|
78
|
+
-------
|
|
79
|
+
str
|
|
80
|
+
Simplified relative path
|
|
81
|
+
"""
|
|
82
|
+
base_path = _os.getcwd()
|
|
83
|
+
relative_sdir = _os.path.relpath(sdir, base_path) if base_path else sdir
|
|
84
|
+
simplified_path = relative_sdir.replace("scripts/", "./scripts/").replace(
|
|
85
|
+
"RUNNING/", ""
|
|
86
|
+
)
|
|
87
|
+
# Remove date-time pattern and random string
|
|
88
|
+
simplified_path = re.sub(
|
|
89
|
+
r"\d{4}Y-\d{2}M-\d{2}D-\d{2}h\d{2}m\d{2}s_\w+/?$", "", simplified_path
|
|
90
|
+
)
|
|
91
|
+
return simplified_path
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def clear_python_log_dir(log_dir: str) -> None:
|
|
95
|
+
"""Clear Python log directory."""
|
|
96
|
+
try:
|
|
97
|
+
if _os.path.exists(log_dir):
|
|
98
|
+
_os.system(f"rm -rf {log_dir}")
|
|
99
|
+
except Exception as e:
|
|
100
|
+
print(f"Failed to clear directory {log_dir}: {e}")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def print_header(
|
|
104
|
+
ID: str,
|
|
105
|
+
PID: int,
|
|
106
|
+
file: str,
|
|
107
|
+
args: Any,
|
|
108
|
+
configs: Dict[str, Any],
|
|
109
|
+
verbose: bool = True,
|
|
110
|
+
) -> None:
|
|
111
|
+
"""Prints formatted header with scitex version, ID, and PID information."""
|
|
112
|
+
if args is not None and hasattr(args, "_get_kwargs"):
|
|
113
|
+
args_str = "Arguments:"
|
|
114
|
+
for arg, value in args._get_kwargs():
|
|
115
|
+
args_str += f"\n {arg}: {value}"
|
|
116
|
+
else:
|
|
117
|
+
args_str = "Arguments: None"
|
|
118
|
+
|
|
119
|
+
_printc(
|
|
120
|
+
(f"SciTeX v{get_scitex_version()} | {ID} (PID: {PID})\n\n{file}\n\n{args_str}"),
|
|
121
|
+
char="=",
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
sleep(1)
|
|
125
|
+
if verbose:
|
|
126
|
+
from pprint import pformat
|
|
127
|
+
|
|
128
|
+
config_str = pformat(configs.to_dict())
|
|
129
|
+
logger.info(f"\n{'-' * 40}\n\n{config_str}\n\n{'-' * 40}\n")
|
|
130
|
+
sleep(1)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def format_diff_time(diff_time) -> str:
|
|
134
|
+
"""Format time difference as HH:MM:SS."""
|
|
135
|
+
total_seconds = int(diff_time.total_seconds())
|
|
136
|
+
hours = total_seconds // 3600
|
|
137
|
+
minutes = (total_seconds % 3600) // 60
|
|
138
|
+
seconds = total_seconds % 60
|
|
139
|
+
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def process_timestamp(CONFIG, verbose=True):
|
|
143
|
+
"""Process session timestamps."""
|
|
144
|
+
try:
|
|
145
|
+
CONFIG["END_DATETIME"] = datetime.now()
|
|
146
|
+
CONFIG["RUN_DURATION"] = format_diff_time(
|
|
147
|
+
CONFIG["END_DATETIME"] - CONFIG["START_DATETIME"]
|
|
148
|
+
)
|
|
149
|
+
if verbose:
|
|
150
|
+
logger.info(
|
|
151
|
+
f"\nSTART TIME: {CONFIG['START_DATETIME']}\n"
|
|
152
|
+
f"END TIME: {CONFIG['END_DATETIME']}\n"
|
|
153
|
+
f"RUN DURATION: {CONFIG['RUN_DURATION']}\n"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
except Exception as e:
|
|
157
|
+
print(e)
|
|
158
|
+
|
|
159
|
+
return CONFIG
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def args_to_str(args_dict) -> str:
|
|
163
|
+
"""Convert args dictionary to formatted string."""
|
|
164
|
+
if args_dict:
|
|
165
|
+
max_key_length = max(len(str(k)) for k in args_dict.keys())
|
|
166
|
+
return "\n".join(
|
|
167
|
+
f"{str(k):<{max_key_length}} : {str(v)}"
|
|
168
|
+
for k, v in sorted(args_dict.items())
|
|
169
|
+
)
|
|
170
|
+
else:
|
|
171
|
+
return ""
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def escape_ansi_from_log_files(log_files) -> None:
|
|
175
|
+
"""Remove ANSI escape sequences from log files."""
|
|
176
|
+
ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
|
|
177
|
+
|
|
178
|
+
for f in log_files:
|
|
179
|
+
with open(f, encoding="utf-8") as file:
|
|
180
|
+
content = file.read()
|
|
181
|
+
cleaned_content = ansi_escape.sub("", content)
|
|
182
|
+
with open(f, "w", encoding="utf-8") as file:
|
|
183
|
+
file.write(cleaned_content)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
# EOF
|
scitex/session/_manager.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
2
|
# Timestamp: "2025-08-21 20:36:50 (ywatanabe)"
|
|
4
3
|
# File: /home/ywatanabe/proj/SciTeX-Code/src/scitex/session/_manager.py
|
|
5
4
|
# ----------------------------------------
|
|
6
5
|
from __future__ import annotations
|
|
6
|
+
|
|
7
7
|
import os
|
|
8
8
|
|
|
9
9
|
__FILE__ = __file__
|
|
@@ -22,7 +22,12 @@ class SessionManager:
|
|
|
22
22
|
def __init__(self):
|
|
23
23
|
self.active_sessions = {}
|
|
24
24
|
|
|
25
|
-
def create_session(
|
|
25
|
+
def create_session(
|
|
26
|
+
self,
|
|
27
|
+
session_id: str,
|
|
28
|
+
config: Dict[str, Any],
|
|
29
|
+
script_path: str = None,
|
|
30
|
+
) -> None:
|
|
26
31
|
"""Register a new session.
|
|
27
32
|
|
|
28
33
|
Parameters
|
|
@@ -31,24 +36,56 @@ class SessionManager:
|
|
|
31
36
|
Unique identifier for the session
|
|
32
37
|
config : Dict[str, Any]
|
|
33
38
|
Session configuration dictionary
|
|
39
|
+
script_path : str, optional
|
|
40
|
+
Path to the script being run
|
|
34
41
|
"""
|
|
35
42
|
self.active_sessions[session_id] = {
|
|
36
43
|
"config": config,
|
|
37
44
|
"start_time": datetime.now(),
|
|
38
45
|
"status": "running",
|
|
46
|
+
"script_path": script_path,
|
|
39
47
|
}
|
|
40
48
|
|
|
41
|
-
|
|
49
|
+
# Start verification tracking (silent fail)
|
|
50
|
+
try:
|
|
51
|
+
from scitex.verify import on_session_start
|
|
52
|
+
|
|
53
|
+
on_session_start(
|
|
54
|
+
session_id=session_id,
|
|
55
|
+
script_path=script_path,
|
|
56
|
+
)
|
|
57
|
+
except Exception:
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
def close_session(
|
|
61
|
+
self,
|
|
62
|
+
session_id: str,
|
|
63
|
+
status: str = "success",
|
|
64
|
+
exit_code: int = 0,
|
|
65
|
+
) -> None:
|
|
42
66
|
"""Mark a session as closed.
|
|
43
67
|
|
|
44
68
|
Parameters
|
|
45
69
|
----------
|
|
46
70
|
session_id : str
|
|
47
71
|
Unique identifier for the session to close
|
|
72
|
+
status : str, optional
|
|
73
|
+
Final status (success, failed, error)
|
|
74
|
+
exit_code : int, optional
|
|
75
|
+
Exit code of the session
|
|
48
76
|
"""
|
|
49
77
|
if session_id in self.active_sessions:
|
|
50
78
|
self.active_sessions[session_id]["status"] = "closed"
|
|
51
79
|
self.active_sessions[session_id]["end_time"] = datetime.now()
|
|
80
|
+
self.active_sessions[session_id]["exit_code"] = exit_code
|
|
81
|
+
|
|
82
|
+
# Stop verification tracking (silent fail)
|
|
83
|
+
try:
|
|
84
|
+
from scitex.verify import on_session_close
|
|
85
|
+
|
|
86
|
+
on_session_close(status=status, exit_code=exit_code)
|
|
87
|
+
except Exception:
|
|
88
|
+
pass
|
|
52
89
|
|
|
53
90
|
def get_active_sessions(self) -> Dict[str, Any]:
|
|
54
91
|
"""Get all active sessions.
|
scitex/session/template.py
CHANGED
scitex/template/__init__.py
CHANGED
|
@@ -22,6 +22,7 @@ from .clone_pip_project import TEMPLATE_REPO_URL as PIP_PROJECT_URL
|
|
|
22
22
|
from .clone_pip_project import clone_pip_project
|
|
23
23
|
from .clone_research import TEMPLATE_REPO_URL as RESEARCH_URL
|
|
24
24
|
from .clone_research import clone_research
|
|
25
|
+
from .clone_research_minimal import clone_research_minimal
|
|
25
26
|
from .clone_singularity import TEMPLATE_REPO_URL as SINGULARITY_URL
|
|
26
27
|
from .clone_singularity import clone_singularity
|
|
27
28
|
from .clone_writer_directory import (
|
|
@@ -54,9 +55,24 @@ def get_available_templates_info():
|
|
|
54
55
|
... print(f"{template['name']}: {template['description']}")
|
|
55
56
|
"""
|
|
56
57
|
return [
|
|
58
|
+
{
|
|
59
|
+
"id": "research_minimal",
|
|
60
|
+
"name": "Research Minimal",
|
|
61
|
+
"description": "Minimal SciTeX structure with writer, scholar, visualizer, and console",
|
|
62
|
+
"github_url": RESEARCH_URL,
|
|
63
|
+
"branch": "minimal",
|
|
64
|
+
"use_case": "Focused research workflow with essential SciTeX modules only",
|
|
65
|
+
"features": [
|
|
66
|
+
"scitex/writer/ - LaTeX manuscript writing",
|
|
67
|
+
"scitex/scholar/ - Bibliography management",
|
|
68
|
+
"scitex/visualizer/ - Figure creation",
|
|
69
|
+
"scitex/console/ - Code execution",
|
|
70
|
+
"scitex/management/ - Project management",
|
|
71
|
+
],
|
|
72
|
+
},
|
|
57
73
|
{
|
|
58
74
|
"id": "research",
|
|
59
|
-
"name": "Research Project",
|
|
75
|
+
"name": "Research Project (Full)",
|
|
60
76
|
"description": "Full scientific workflow structure for research projects",
|
|
61
77
|
"github_url": RESEARCH_URL,
|
|
62
78
|
"use_case": "Scientific research with data analysis, experiments, and paper writing",
|
|
@@ -113,6 +129,7 @@ def get_available_templates_info():
|
|
|
113
129
|
|
|
114
130
|
__all__ = [
|
|
115
131
|
"clone_research",
|
|
132
|
+
"clone_research_minimal",
|
|
116
133
|
"clone_pip_project",
|
|
117
134
|
"clone_singularity",
|
|
118
135
|
"clone_writer_directory",
|
|
@@ -74,7 +74,7 @@ def main(
|
|
|
74
74
|
CONFIG=stx.INJECTED, # Session config
|
|
75
75
|
plt=stx.INJECTED, # Pre-configured matplotlib
|
|
76
76
|
COLORS=stx.INJECTED, # Color palette
|
|
77
|
-
|
|
77
|
+
rngg=stx.INJECTED, # Random generator
|
|
78
78
|
logger=stx.INJECTED, # Logger
|
|
79
79
|
):
|
|
80
80
|
"""Example plotting with session management."""
|
|
@@ -91,7 +91,7 @@ def main(
|
|
|
91
91
|
CONFIG=stx.INJECTED, # Auto-injected from ./config/*.yaml
|
|
92
92
|
plt=stx.INJECTED, # Pre-configured matplotlib
|
|
93
93
|
COLORS=stx.INJECTED, # Color palette
|
|
94
|
-
|
|
94
|
+
rngg=stx.INJECTED, # Random number generator
|
|
95
95
|
logger=stx.INJECTED, # Session logger
|
|
96
96
|
):
|
|
97
97
|
"""
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/template/clone_research_minimal.py
|
|
3
|
+
# ----------------------------------------
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
__FILE__ = "./src/scitex/template/clone_research_minimal.py"
|
|
9
|
+
__DIR__ = os.path.dirname(__FILE__)
|
|
10
|
+
# ----------------------------------------
|
|
11
|
+
|
|
12
|
+
"""
|
|
13
|
+
Create a new minimal research project from the scitex_template_research template.
|
|
14
|
+
|
|
15
|
+
Uses the 'minimal' branch which contains only the scitex/ directory with:
|
|
16
|
+
- writer/ - LaTeX manuscript writing
|
|
17
|
+
- scholar/ - Bibliography management
|
|
18
|
+
- visualizer/ - Figure creation
|
|
19
|
+
- console/ - Code execution
|
|
20
|
+
- management/ - Project management
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import sys
|
|
24
|
+
from typing import Optional
|
|
25
|
+
|
|
26
|
+
from ._clone_project import clone_project
|
|
27
|
+
|
|
28
|
+
TEMPLATE_REPO_URL = "https://github.com/ywatanabe1989/scitex-minimal-template.git"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def clone_research_minimal(
|
|
32
|
+
project_dir: str,
|
|
33
|
+
git_strategy: Optional[str] = "child",
|
|
34
|
+
branch: Optional[str] = None,
|
|
35
|
+
tag: Optional[str] = None,
|
|
36
|
+
) -> bool:
|
|
37
|
+
"""
|
|
38
|
+
Create a new minimal research project from the scitex-minimal-template.
|
|
39
|
+
|
|
40
|
+
This template contains only the essential scitex/ directory structure:
|
|
41
|
+
- writer/ - Full LaTeX manuscript writing with compilation scripts
|
|
42
|
+
- scholar/ - Bibliography management
|
|
43
|
+
- visualizer/ - Figure creation
|
|
44
|
+
- console/ - Code execution
|
|
45
|
+
|
|
46
|
+
Parameters
|
|
47
|
+
----------
|
|
48
|
+
project_dir : str
|
|
49
|
+
Path to project directory (will be created). Can be a simple name like "my_project"
|
|
50
|
+
or a full path like "./projects/my_project"
|
|
51
|
+
git_strategy : str, optional
|
|
52
|
+
Git initialization strategy ('child', 'parent', None). Default is 'child'.
|
|
53
|
+
branch : str, optional
|
|
54
|
+
Specific branch of the template repository to clone.
|
|
55
|
+
tag : str, optional
|
|
56
|
+
Specific tag/release of the template repository to clone.
|
|
57
|
+
|
|
58
|
+
Returns
|
|
59
|
+
-------
|
|
60
|
+
bool
|
|
61
|
+
True if successful, False otherwise
|
|
62
|
+
|
|
63
|
+
Example
|
|
64
|
+
-------
|
|
65
|
+
>>> from scitex.template import clone_research_minimal
|
|
66
|
+
>>> clone_research_minimal("my_research_project")
|
|
67
|
+
>>> clone_research_minimal("./projects/my_project")
|
|
68
|
+
"""
|
|
69
|
+
return clone_project(
|
|
70
|
+
project_dir,
|
|
71
|
+
TEMPLATE_REPO_URL,
|
|
72
|
+
"scitex-minimal-template",
|
|
73
|
+
git_strategy,
|
|
74
|
+
branch=branch,
|
|
75
|
+
tag=tag,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def main(args: list = None) -> None:
|
|
80
|
+
"""
|
|
81
|
+
Command-line interface for clone_research_minimal.
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
args : list, optional
|
|
86
|
+
Command-line arguments. If None, uses sys.argv[1:]
|
|
87
|
+
"""
|
|
88
|
+
if args is None:
|
|
89
|
+
args = sys.argv[1:]
|
|
90
|
+
|
|
91
|
+
if len(args) < 1:
|
|
92
|
+
print("Usage: python -m scitex clone_research_minimal <project-dir>")
|
|
93
|
+
print("")
|
|
94
|
+
print("Arguments:")
|
|
95
|
+
print(" project-dir Path to project directory (will be created)")
|
|
96
|
+
print(" Can be a simple name like 'my_project' or a full path")
|
|
97
|
+
print("")
|
|
98
|
+
print("Example:")
|
|
99
|
+
print(" python -m scitex clone_research_minimal my_research_project")
|
|
100
|
+
sys.exit(1)
|
|
101
|
+
|
|
102
|
+
project_dir = args[0]
|
|
103
|
+
|
|
104
|
+
success = clone_research_minimal(project_dir)
|
|
105
|
+
sys.exit(0 if success else 1)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
if __name__ == "__main__":
|
|
109
|
+
main()
|
|
110
|
+
|
|
111
|
+
# EOF
|