generated from SchmollerLab/Cell_ACDC
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstall_CellACDC.py
More file actions
683 lines (592 loc) Β· 30.1 KB
/
install_CellACDC.py
File metadata and controls
683 lines (592 loc) Β· 30.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
try:
import os
import subprocess
import argparse
import json
import datetime
import sys
import traceback
import platform
import re
import time
import pathlib
except ImportError as e:
print(f"β Import error: {e}")
input("Press Enter to close...")
sys.exit(1)
def run_subprocess_with_logging(cmd, env=None):
"""Run subprocess and capture all output to log file with real-time streaming"""
if isinstance(cmd, str):
cmd = [cmd]
print("-" * 40)
print(f"π§ Running command: {' '.join(cmd)}")
print(f" Working Directory: {os.getcwd()}")
print(f" Timestamp: {datetime.datetime.now().strftime('%H:%M:%S')}")
print("π€ Command output (streaming):")
max_tries = 3
tries_remaining = max_tries
while tries_remaining > 0:
try:
if tries_remaining < max_tries:
print(f"π Retrying command (attempt {max_tries - tries_remaining + 1} of {max_tries})...")
start_time = datetime.datetime.now()
# Start the process with pipes for real-time output
process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, # Merge stderr into stdout for unified output
text=True,
encoding='utf-8',
errors='replace',
bufsize=1,
universal_newlines=True,
env=env
)
# Stream output in real-time
output_lines = []
while True:
output = process.stdout.readline()
if output == '' and process.poll() is not None:
break
if output:
# Print to console (and thus to log via Tee)
print(output.rstrip())
output_lines.append(output)
# Wait for process to complete and get return code
return_code = process.poll()
end_time = datetime.datetime.now()
duration = (end_time - start_time).total_seconds()
if return_code == 0:
print(f" β
Command completed successfully in {duration:.2f} seconds")
print("-" * 40)
return # Success, exit the retry loop
else:
print(f"β Command failed with return code {return_code} after {duration:.2f} seconds")
tries_remaining -= 1
if tries_remaining > 0:
print(f"β³ {tries_remaining} tries remaining. Waiting 5 seconds before retry...")
time.sleep(5)
else:
print(f"β All {max_tries} attempts failed")
raise subprocess.CalledProcessError(return_code, cmd, output=''.join(output_lines))
except subprocess.CalledProcessError as e:
if tries_remaining <= 1:
print(f"β Command failed with return code {e.returncode} after all retries")
print("-" * 40)
raise e
else:
tries_remaining -= 1
print(f"β Command failed with return code {e.returncode}")
print(f"β³ {tries_remaining} tries remaining. Waiting 5 seconds before retry...")
time.sleep(5)
except Exception as e:
if tries_remaining <= 1:
print(f"β Error running subprocess: {e}")
print("-" * 40)
raise e
else:
tries_remaining -= 1
print(f"β Error running subprocess: {e}")
print(f"β³ {tries_remaining} tries remaining. Waiting 5 seconds before retry...")
time.sleep(5)
class Tee:
def __init__(self, *files):
self.files = files
def write(self, obj):
for f in self.files:
try:
if isinstance(obj, bytes):
obj = obj.decode('utf-8', errors='replace')
f.write(obj)
f.flush()
except (UnicodeEncodeError, ValueError):
if f != sys.__stdout__ and f != sys.__stderr__:
try:
sys.__stdout__.write(obj)
sys.__stdout__.flush()
except:
pass
def flush(self):
for f in self.files:
try:
f.flush()
except (ValueError, AttributeError):
pass
def get_install_params(executable_dir):
command_file = os.path.join(executable_dir, "installation_command.txt")
print(f"π Loading values from command file: {command_file}")
try:
with open(command_file, 'r', encoding='utf-8', errors='replace') as f:
content = f.read()
# Extract the command line from the file content
# Look for the line that contains the actual command with flags
command_line = ""
for line in content.split('\n'):
if '--target' in line and 'Cell-ACDC-installer.exe' in line:
command_line = line.strip()
break
if command_line:
print(f"π Found command: {command_line}")
# Extract all parameters using a single regex with multiple capture groups
pattern = (r'--target\s+"([^"]+)".*?'
r'--use_github\s+"([^"]+)".*?'
r'--version\s+"([^"]+)".*?'
r'--python_path\s+"([^"]+)".*?'
r'--embeddedpyflag\s+"([^"]+)".*?'
r'--pyversion\s+(\S+).*?'
r'--custom_CellACDC_path\s+"([^"]+)".*?'
r'--install_models_list\s+"([^"]+)"')
match = re.search(pattern, command_line)
if match:
target_dir = match.group(1)
use_github = match.group(2).lower() == 'true'
cellacdc_version = match.group(3)
python_path = match.group(4)
is_embedded_python = match.group(5).lower() == 'true'
pyversion = match.group(6)
custom_CellACDC_path = match.group(7)
install_models_list = match.group(8)
print(f" Target: {target_dir}")
print(f" Use GitHub: {use_github}")
print(f" Version: {cellacdc_version}")
print(f" Python Path: {python_path}")
print(f" Embedded Python: {is_embedded_python}")
print(f" Python Version: {pyversion}")
print(f" Custom CellACDC Path: {custom_CellACDC_path}")
print(f" Install Models List: {install_models_list}")
else:
print("β οΈ Unable to parse all required parameters from the command line in installation_command.txt.")
print(" Please ensure the file contains all required flags:")
print(""" --target, --use_github, --version, --python_path, --embeddedpyflag, --pyversion, --custom_CellACDC_path, --install_models_list""")
raise ValueError("Failed to extract installation parameters from command. Please check installation_command.txt for completeness and correct formatting.")
print("β
Successfully loaded installation parameters from command file")
else:
print("β οΈ Could not find a valid command line in installation_command.txt.")
print(" Please ensure the file contains a line with all required flags:")
print(" --target, --use_github, --version, --python_path, --embeddedpyflag, --pyversion, --custom_CellACDC_path, --install_models_list")
raise ValueError(
"Could not load installation parameters from command file. Please check installation_command.txt."
)
except FileNotFoundError as e:
print(f"β οΈ installation_command.txt not found at: {command_file}")
print(" Please run the installer with all required flags or ensure installation_command.txt exists in the executable directory.")
raise e
except Exception as e:
print(f"β οΈ Error reading installation_command.txt: {e}")
print(" Please check the file for correct formatting and required flags.")
raise e
return target_dir, use_github, cellacdc_version, python_path, is_embedded_python, pyversion, custom_CellACDC_path, install_models_list
def setup_logging():
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
log_filename = f"{timestamp}_cellacdc_install.log"
user_home_path = str(pathlib.Path.home())
user_profile_path = os.path.join(user_home_path, 'acdc-appdata')
log_path = os.path.join(user_profile_path, ".acdc-logs", log_filename)
os.makedirs(os.path.dirname(log_path), exist_ok=True)
# Open log file with UTF-8 encoding
log_file = open(log_path, 'w', encoding='utf-8', errors='replace')
# Store original stdout/stderr
original_stdout = sys.stdout
original_stderr = sys.stderr
# Redirect stdout and stderr to both console and log file
sys.stdout = Tee(original_stdout, log_file)
sys.stderr = Tee(original_stderr, log_file)
# Log session header with system information
print("=" * 80)
print(f"Cell-ACDC Installation Session - {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("=" * 80)
print(f"System Information:")
print(f" Platform: {platform.platform()}")
print(f" Python Version: {sys.version}")
print(f" Working Directory: {os.getcwd()}")
print(f" Log File: {log_path}")
print("=" * 80)
print()
print(f"π Installation log will be saved to: {log_path}")
return log_file, original_stdout, original_stderr, log_path
def print_closing_logging(log_path):
"""Print closing message for logging"""
print()
print("=" * 80)
print("Logging session ended.")
print(f"π Installation log can be found at: {log_path}")
print("=" * 80)
print("INSTALLATION SUMMARY")
print("=" * 80)
print(f"Target Directory: {target_dir}")
print(f"Python Path: {python_path}")
print(f"Virtual Environment: {venv_path if not is_conda else conda_venv_path}")
print(f"Using Conda: {is_conda}")
print(f"Installation Source: {'GitHub' if use_github else 'PyPI'}")
if not use_github:
print(f"Version: {cellacdc_version}")
print(f"Session End: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("=" * 80)
print("β
Installation completed successfully!")
def install_models_list_converter(install_models_list):
cmd_extension = []
acdc_flags = []
if isinstance(install_models_list, str):
install_models_list = install_models_list.strip('"').strip("'").strip() # Clean up quotes if present
install_models_list = install_models_list.split(',')
if install_models_list[0] is None or install_models_list[0] == "none":
return "", acdc_flags
for model in install_models_list:
model = model.strip().lower()
model = model.strip('"').strip("'").strip() # Clean up quotes if present
if model is None or model == "none":
continue
if model == "cellpose3":
cmd_extension.append("cellpose3")
acdc_flags.append("--cpModelsDownload")
elif model == "stardist":
cmd_extension.append("stardist")
acdc_flags.append("--StarDistModelsDownload")
elif model == "yeaz":
cmd_extension.append("yeaz")
acdc_flags.append("--YeaZModelsDownload")
elif model == "trackastra":
cmd_extension.append("trackastra")
acdc_flags.append("--TrackastraModelsDownload")
elif model == "deepsea":
cmd_extension.append("deepsea")
acdc_flags.append("--DeepSeaModelsDownload")
else:
print(f"β οΈ Unknown model '{model}' specified in install_models_list. Skipping.")
if cmd_extension:
cmd = ",".join(cmd_extension)
cmd = f"[{cmd}]"
else:
cmd = ""
return cmd, acdc_flags
if __name__ == "__main__":
try:
# Set up logging at the beginning of your script
log_file, original_stdout, original_stderr, log_path = setup_logging()
repo_url = "https://github.com/SchmollerLab/Cell_ACDC"
clone_path = "Cell_ACDC"
git_path = "portable_git" # only for windows where git is not installed
git_path = os.path.join(git_path, "cmd", "git.exe")
parser = argparse.ArgumentParser()
parser.add_argument('--target', help='Target install path')
parser.add_argument('--use_github', help='Use GitHub clone (true/false)')
parser.add_argument('--version', help='CellACDC version to install from PyPI')
parser.add_argument('--python_path', help='Path to already installed Python')
parser.add_argument('--embeddedpyflag', help='Path to Python installer executable (if applicable)')
parser.add_argument('--pyversion', help='Python version to use for conda environment')
parser.add_argument('--custom_CellACDC_path', help='Custom path to CellACDC repository clone (if applicable)')
parser.add_argument('--install_models_list', help='Install models list', default="NOT SET")
args = parser.parse_args()
target_dir = args.target if args.target else None
use_github = args.use_github.lower() == 'true' if args.use_github else None
cellacdc_version = args.version if args.version else None
is_embedded_python = args.embeddedpyflag if args.embeddedpyflag else None
python_path = args.python_path if args.python_path else None
pyversion = args.pyversion if args.pyversion else None
custom_CellACDC_path = args.custom_CellACDC_path if args.custom_CellACDC_path else None
install_models_list = args.install_models_list if args.install_models_list else None
if install_models_list is None or install_models_list.lower() == "none":
install_models_list = []
elif isinstance(install_models_list, str) and install_models_list.lower() == "not set":
install_models_list = None
# Check if any main install flags are provided
install_flags = [
('target', target_dir),
('use_github', use_github),
('version', cellacdc_version),
('python_path', python_path),
('pyversion', pyversion),
('embeddedpyflag', is_embedded_python),
('custom_CellACDC_path', custom_CellACDC_path),
# ('install_models_list', install_models_list)
]
# Get list of provided and missing flags
provided_flags = [name for name, value in install_flags if value is not None]
missing_flags = [name for name, value in install_flags if value is None]
# If any main install flags are provided, all must be provided
if provided_flags and missing_flags:
error_msg = (
f"β Invalid argument combination: If any main install flags are provided, "
f"all must be provided.\n"
f" Provided flags: {', '.join(['--' + flag for flag in provided_flags])}\n"
f" Missing flags: {', '.join(['--' + flag for flag in missing_flags])}\n"
f" Either provide all flags for automated installation, or provide none for interactive mode."
)
print(error_msg)
raise ValueError(error_msg)
# Determine and announce the installation mode
flag_mode = all(value is not None for _, value in install_flags)
# Get the executable path for PyInstaller
if getattr(sys, 'frozen', False):
# Running as PyInstaller executable
executable_path = sys.executable
executable_dir = os.path.dirname(sys.executable)
print(f"π¦ Running from PyInstaller executable: {executable_path}")
else:
# Running as Python script
executable_path = os.path.abspath(__file__)
executable_dir = os.path.dirname(executable_path)
print(f"π Running as Python script: {executable_path}")
if not flag_mode:
(target_dir, use_github, cellacdc_version, python_path,
is_embedded_python, pyversion, custom_CellACDC_path,
install_models_list) = get_install_params(executable_dir)
if python_path:
python_path = os.path.abspath(python_path) # Ensure absolute path, and format correctly for Windows
if custom_CellACDC_path.lower() == "default":
custom_CellACDC_path = None
if isinstance(custom_CellACDC_path, str):
custom_CellACDC_path = custom_CellACDC_path.strip('"').strip("'").strip() # Clean up quotes if present
use_whl = False
use_custom_CellACDC = False
if custom_CellACDC_path:
use_whl = custom_CellACDC_path.endswith('.whl')
custom_CellACDC_path = os.path.abspath(custom_CellACDC_path) # Ensure absolute path for custom path
use_custom_CellACDC = True if not use_whl else False
operating_system = platform.system().lower()
is_windows = operating_system == "windows"
clone_path = os.path.join(target_dir, clone_path)
# Log all command line arguments for debugging
if flag_mode:
print("Command Line Arguments:")
for arg, value in vars(args).items():
print(f" --{arg}: {value}")
print()
if use_github:
if os.path.exists(clone_path):
print(f"β οΈ CellACDC repository already exists at {clone_path}. Skipping clone.")
else:
print(f"π₯ Cloning CellACDC repository from {repo_url} to {clone_path} (This may take a while)...")
print(f" Target directory: {clone_path}")
try:
# Use porcelain.clone with explicit configuration to avoid interactive prompts
if is_windows:
git_prefix = os.path.abspath(os.path.join(target_dir, git_path))
else:
git_prefix = "git"
cmd = [git_prefix, "clone", repo_url, clone_path]
run_subprocess_with_logging(cmd)
cmd = [git_prefix, "config", "--global", "--add",
"safe.directory", clone_path]
run_subprocess_with_logging(cmd)
# Add a small delay to ensure file system operations are complete
print(" Waiting for file system operations to complete...")
time.sleep(2)
except Exception as e:
print(f"β Git clone failed: {e}")
raise
if use_custom_CellACDC or use_github:
clone_path = custom_CellACDC_path if custom_CellACDC_path else clone_path
print(f"π Using CellACDC at: {clone_path}")
# Verify the clone path contains a valid Python package with retry logic
print(f"π Verifying package structure at: {clone_path}")
max_retries = 5
retry_count = 0
while retry_count < max_retries:
if os.path.exists(clone_path):
print(f" Directory exists: {clone_path}")
try:
files_in_dir = os.listdir(clone_path)
print(f" Contents: {files_in_dir}")
# Check for setup.py or pyproject.toml
setup_py = os.path.join(clone_path, "setup.py")
pyproject_toml = os.path.join(clone_path, "pyproject.toml")
if os.path.exists(pyproject_toml):
print(f" β
Found pyproject.toml")
break
else:
print(f" β οΈ No pyproject.toml found on attempt {retry_count + 1}")
if retry_count < max_retries - 1:
print(f" Waiting 2 seconds before retry...")
time.sleep(2)
retry_count += 1
continue
else:
print(f" Available files: {[f for f in files_in_dir if f.endswith(('.py', '.toml', '.cfg'))]}")
break
except Exception as e:
print(f" β οΈ Error accessing directory on attempt {retry_count + 1}: {e}")
if retry_count < max_retries - 1:
print(f" Waiting 2 seconds before retry...")
time.sleep(2)
retry_count += 1
continue
else:
raise
else:
print(f" β Clone path does not exist on attempt {retry_count + 1}: {clone_path}")
if retry_count < max_retries - 1:
print(f" Waiting 2 seconds before retry...")
time.sleep(2)
retry_count += 1
continue
else:
raise FileNotFoundError(f"Clone path not found after {max_retries} attempts: {clone_path}")
elif use_whl:
clone_path = os.path.abspath(custom_CellACDC_path) # Ensure absolute path for pip install
if isinstance(is_embedded_python, str):
if is_embedded_python.lower() == 'true':
is_embedded_python = True
if is_embedded_python is True:
# Cross-platform Python executable path
python_exe = "python.exe" if is_windows else "python"
python_path = os.path.join(target_dir, "miniforge", python_exe)
is_conda = python_path.lower().find("miniforge") != -1 or python_path.lower().find("conda") != -1
if is_conda:
folder = os.path.dirname(python_path)
# Cross-platform conda executable path
if is_windows:
conda_path = os.path.join(folder, "Scripts", "conda.exe")
else:
conda_path = os.path.join(folder, "bin", "conda")
conda_path = os.path.abspath(conda_path) # Ensure absolute path for conda
# conda_path = f'"{conda_path}"' # Ensure proper quoting for Windows paths
# python_path = f'"{python_path}"' # Ensure proper quoting for Windows paths'
cmd_extension, acdc_flags = install_models_list_converter(install_models_list)
if not is_conda:
venv_path = os.path.join(target_dir, "venv")
venv_path = os.path.abspath(venv_path) # Ensure absolute path for venv
print(f"π± Creating venv at: {venv_path}")
print(f"Using Python at: {python_path}")
run_subprocess_with_logging([python_path, "-m", "venv", venv_path])
print("β
venv created.")
# Cross-platform pip executable path
if is_windows:
pip_path = os.path.join(venv_path, "Scripts", "pip.exe")
else:
pip_path = os.path.join(venv_path, "bin", "pip")
if use_github or custom_CellACDC_path:
print("π οΈ Installing CellACDC and dependencies...")
print(f" Using Cell-ACDC path: {clone_path}")
clone_path = os.path.abspath(clone_path) # Ensure absolute path for pip install
print(f" Absolute path: {clone_path}")
cmd = [pip_path, "install", f"{clone_path}{cmd_extension if cmd_extension else ''}"]
if not use_whl:
cmd.insert(-1, "-e")
run_subprocess_with_logging(cmd)
else:
print(f"π οΈ Downloading and installing CellACDC v{cellacdc_version} and dependencies...")
# Install specific version from PyPI
run_subprocess_with_logging([
pip_path, "install", f"cellacdc{cmd_extension if cmd_extension else ''}=={cellacdc_version}",
])
print("β
Pip installation completed.")
elif is_conda:
conda_venv_path = os.path.join(target_dir, "conda_venv")
conda_venv_path = os.path.abspath(conda_venv_path) # Ensure absolute path for conda venv
print(f"π± Creating conda venv: {conda_venv_path}")
print(f"Using conda/miniforge Python at: {python_path}")
condarc_path = os.path.join(target_dir, ".condarc")
if os.path.exists(condarc_path):
env = os.environ.copy()
env["CONDARC"] = condarc_path
else:
env = None
# Create conda environment with Python
run_subprocess_with_logging([
conda_path,
"create", "-y",
"-p", conda_venv_path,
f"python={pyversion}",
"-c", "conda-forge",
], env=env)
print("β
Conda environment created.")
if use_github or custom_CellACDC_path:
print("π οΈ Installing CellACDC and dependencies...")
print(f" Using Cell-ACDC path: {clone_path}")
clone_path = os.path.abspath(clone_path) # Ensure absolute path for pip install
print(f" Absolute path: {clone_path}")
# Install in editable mode from local clone
cmd = [
conda_path,
"run", "-p", conda_venv_path,
"pip", "install", f"{clone_path}{cmd_extension if cmd_extension else ''}"
]
if not use_whl:
cmd.insert(-1, "-e")
run_subprocess_with_logging(cmd, env=env)
else:
print(f"π οΈ Downloading and installing CellACDC v{cellacdc_version} and dependencies...")
# Install specific version from PyPI
run_subprocess_with_logging([
conda_path,
"run", "-p", conda_venv_path,
"pip", "install", f"cellacdc{cmd_extension if cmd_extension else ''}=={cellacdc_version}",
], env=env)
print("β
Conda pip installation completed.")
print("π¦ Saving installation details...")
install_details = {
"target_dir": target_dir,
"venv_path": venv_path if not is_conda else conda_venv_path,
"conda": is_conda,
"clone_path": clone_path if use_github else "",
"use_github": use_github,
"version": cellacdc_version,
"conda_path": conda_path if is_conda else "",
}
with open(os.path.join(target_dir, "install_details.json"), "w") as f:
json.dump(install_details, f, indent=4)
print("β
Installation details saved to install_details.json.")
print("π οΈ Launching CellACDC for internal setup...")
# Cross-platform ACDC executable paths
cmd = ["-y", "--install_details",
os.path.join(target_dir, "install_details.json")]
cmd += acdc_flags
if not is_conda:
if is_windows:
acdc_exec_path = os.path.join(venv_path, "Scripts", "acdc.exe")
else:
acdc_exec_path = os.path.join(venv_path, "bin", "acdc")
cmd = [acdc_exec_path] + cmd
print(f" Running cmd: {cmd}")
subprocess.run(cmd)
else:
if is_windows:
acdc_exec_path = os.path.join(conda_venv_path, "Scripts", "acdc.exe")
else:
acdc_exec_path = os.path.join(conda_venv_path, "bin", "acdc")
cmd = [acdc_exec_path] + cmd
print(f" Running cmd: {cmd}")
subprocess.run(cmd, env=env)
print("β
CellACDC internal setup completed.")
# Log final session summary
print_closing_logging(log_path)
except Exception as e:
# Restore original stdout/stderr for error handling
sys.stdout = original_stdout
sys.stderr = original_stderr
print()
print("=" * 80)
print("INSTALLATION ERROR")
print("=" * 80)
print("β An error occurred during installation.")
print(f"Error: {str(e)}")
print(f"Error Time: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
traceback.print_exc()
# Save error to log file
try:
with open(log_path, 'a', encoding='utf-8', errors='replace') as f:
f.write(f"\n\n{'='*80}\n")
f.write(f"INSTALLATION ERROR - {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"{'='*80}\n")
f.write(f"β ERROR: {str(e)}\n")
f.write(f"Traceback:\n{traceback.format_exc()}\n")
f.write(f"{'='*80}\n")
print(f"π Full installation log saved to: {log_path}")
print("=" * 80)
except:
print("β οΈ Could not save error log.")
print("Log files are saved in the following directory:")
print(f"π {os.path.dirname(log_path)}")
print("Please copy the two newest log files, and report this issue to the CellACDC team at:")
print(repo_url)
input("β!!!CELL-ACDC SETUP IS IN ERROR STATE!!!β Press Enter to close this window...")
finally:
# Safely close log file and restore stdout/stderr
try:
sys.stdout = original_stdout
sys.stderr = original_stderr
if 'log_file' in locals() and not log_file.closed:
log_file.close()
except:
pass