starbash 0.2.0__tar.gz → 0.2.4__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.
- {starbash-0.2.0 → starbash-0.2.4}/PKG-INFO +6 -4
- {starbash-0.2.0 → starbash-0.2.4}/README.md +3 -2
- {starbash-0.2.0 → starbash-0.2.4}/pyproject.toml +28 -4
- {starbash-0.2.0 → starbash-0.2.4}/src/repo/repo.py +6 -1
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/__init__.py +1 -1
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/analytics.py +3 -3
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/app.py +36 -6
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/commands/process.py +1 -1
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/commands/user.py +2 -4
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/doit.py +1 -2
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/exception.py +11 -4
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/paths.py +7 -2
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/processed_target.py +4 -102
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/processing.py +39 -90
- starbash-0.2.4/src/starbash/sim_siril/__init__.py +6 -0
- starbash-0.2.4/src/starbash/sim_siril/connection.py +79 -0
- starbash-0.2.4/src/starbash/sim_siril/enums.py +316 -0
- starbash-0.2.4/src/starbash/sim_siril/utility.py +32 -0
- starbash-0.2.4/src/starbash/stage_utils.py +45 -0
- starbash-0.2.4/src/starbash/stages.py +260 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/tool/context.py +74 -3
- starbash-0.2.4/src/starbash/tool/python.py +204 -0
- starbash-0.2.4/src/starbash/ui/main.py +92 -0
- starbash-0.2.0/src/starbash/tool/python.py +0 -84
- {starbash-0.2.0 → starbash-0.2.4}/LICENSE +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/repo/__init__.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/repo/http_client.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/repo/manager.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/aliases.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/check_version.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/commands/__init__.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/commands/info.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/commands/repo.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/commands/select.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/database.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/defaults/__init__.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/defaults/starbash.toml +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/doit_types.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/dwarf3.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/filtering.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/icon.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/linux.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/main.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/os.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/parameters.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/processing_like.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/recipes/README.md +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/recipes/__init__.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/recipes/osc.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/rich.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/safety.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/score.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/selection.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/templates/__init__.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/templates/repo/master.toml +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/templates/repo/processed.toml +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/templates/target/master.toml +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/templates/target/processed.toml +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/templates/userconfig.toml +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/toml.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/tool/__init__.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/tool/base.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/tool/graxpert.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/tool/siril.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/url.py +0 -0
- {starbash-0.2.0 → starbash-0.2.4}/src/starbash/windows.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: starbash
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: A tool for automating/standardizing/sharing astrophotography workflows.
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Author: Kevin Hester
|
|
@@ -12,13 +12,14 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.14
|
|
13
13
|
Requires-Dist: astropy (>=7.1.1,<8.0.0)
|
|
14
14
|
Requires-Dist: doit (>=0.36.0,<0.37.0)
|
|
15
|
-
Requires-Dist: graxpert[cpuonly] (>=3.2.
|
|
15
|
+
Requires-Dist: graxpert[cpuonly] (>=3.2.0a2,<4.0.0)
|
|
16
16
|
Requires-Dist: multidict (>=6.7.0,<7.0.0)
|
|
17
17
|
Requires-Dist: platformdirs (>=4.5.0,<5.0.0)
|
|
18
18
|
Requires-Dist: requests-cache (>=1.2.1,<2.0.0)
|
|
19
19
|
Requires-Dist: restrictedpython (>=8.1,<9.0)
|
|
20
20
|
Requires-Dist: rich (>=14.2.0,<15.0.0)
|
|
21
21
|
Requires-Dist: sentry-sdk (>=2.42.1,<3.0.0)
|
|
22
|
+
Requires-Dist: textual (>=6.8.0,<7.0.0)
|
|
22
23
|
Requires-Dist: tomlkit (>=0.13.3,<0.14.0)
|
|
23
24
|
Requires-Dist: typer (>=0.20.0,<0.21.0)
|
|
24
25
|
Requires-Dist: update-checker (>=0.18.0,<0.19.0)
|
|
@@ -40,7 +41,7 @@ A tool for automating/standardizing/sharing astrophotography workflows.
|
|
|
40
41
|
<img src="https://raw.githubusercontent.com/geeksville/starbash/refs/heads/main/doc/img/icon.jpg" alt="Starbash: Astrophotography workflows simplified" width="30%" align="right" style="margin-bottom: 20px;">
|
|
41
42
|
|
|
42
43
|
* Automatic - with sensible defaults that you can change as needed. Just a few keystrokes to preprocess an entire repo of raw images.
|
|
43
|
-
* Easy - provides a 'seestar-like' autoprocessing all your sessions (by default). You can optionally do manual postprocessing with all this 'grunt work' preprocessing.
|
|
44
|
+
* Easy - provides a 'seestar-like' autoprocessing all your sessions (by default). You can optionally do manual postprocessing with all this 'grunt work' preprocessing automated.
|
|
44
45
|
* Fast<sup>1</sup> - even with large image repositories. Automatic master bias and flat generation. Automatic preprocessing/ stacking/background-elimination with reasonable defaults.
|
|
45
46
|
* Multi-session - by default. So your workflows can stack from multiple nights (and still use the correct flats etc...).
|
|
46
47
|
* Shareable - you can share/use recipes for image preprocessing flows. The eventual goal is a transition away from opaque burried scripts. Recipes can come from multiple repos and you or other orgs can host recipes.
|
|
@@ -85,7 +86,7 @@ If you are interested in alpha-testing we ❤️ you. This README should have e
|
|
|
85
86
|
* Automatically performs **complete** preprocessing on OSC (broadband, narrowband, or dual Duo filter). i.e., gives you 'seestar-level' auto-preprocessing, so you only need to do the (optional) custom post-processing.
|
|
86
87
|
* Uses Siril recipes for its pre-processing operations (support for Pixinsight-based recipes will probably be coming at some point...).
|
|
87
88
|
|
|
88
|
-
### From the alpha 2 (0.2.0) release (2025/12/12
|
|
89
|
+
### From the alpha 2 (0.2.0) release (2025/12/12)
|
|
89
90
|
* Include a stretched 'thumbnail'(FIXME/link/example/thumbnail) jpg proof-of-concept render in the output directories
|
|
90
91
|
* Generates a per-target [report/config file](doc/toml/example/processed-repo/example-m20.toml) which can be customized if the detected defaults or preprocessing are not what you want.
|
|
91
92
|
* '[Recipes](https://github.com/geeksville/starbash-recipes)' are now user-editable - provides repeatable/human-[readable](doc/toml/example/processed-repo/example-m20.toml)/shareable descriptions of all processing steps. [example Siril OSC stacking](https://github.com/geeksville/starbash-recipes/blob/6d255f48591c4991ddc1168ffa5c38050a500771/osc/osc_simple.toml), [example Graxpert BGE](https://github.com/geeksville/starbash-recipes/blob/6d255f48591c4991ddc1168ffa5c38050a500771/graxpert/background.toml), [example thumbnail generation](https://github.com/geeksville/starbash-recipes/blob/6d255f48591c4991ddc1168ffa5c38050a500771/common/thumbnail.toml)...
|
|
@@ -248,6 +249,7 @@ Starbash is a tool agnostic workflow manager. But it wouldn't be possible witho
|
|
|
248
249
|
* [@codegistics](https://github.com/codegistics) for kindly donating Dwarf3 test data and invaluable debugging assistance.
|
|
249
250
|
* The developers of Siril and Graxpert - which are wonderful tools.
|
|
250
251
|
* The [doit](https://pydoit.org/) an **amazing** automation building tool, which substantially simiplified this tool's development.
|
|
252
|
+
* Any parts of the user interface that look good are probably due to the awesome [rich](https://github.com/Textualize/rich) library.
|
|
251
253
|
|
|
252
254
|
## Development
|
|
253
255
|
|
|
@@ -11,7 +11,7 @@ A tool for automating/standardizing/sharing astrophotography workflows.
|
|
|
11
11
|
<img src="https://raw.githubusercontent.com/geeksville/starbash/refs/heads/main/doc/img/icon.jpg" alt="Starbash: Astrophotography workflows simplified" width="30%" align="right" style="margin-bottom: 20px;">
|
|
12
12
|
|
|
13
13
|
* Automatic - with sensible defaults that you can change as needed. Just a few keystrokes to preprocess an entire repo of raw images.
|
|
14
|
-
* Easy - provides a 'seestar-like' autoprocessing all your sessions (by default). You can optionally do manual postprocessing with all this 'grunt work' preprocessing.
|
|
14
|
+
* Easy - provides a 'seestar-like' autoprocessing all your sessions (by default). You can optionally do manual postprocessing with all this 'grunt work' preprocessing automated.
|
|
15
15
|
* Fast<sup>1</sup> - even with large image repositories. Automatic master bias and flat generation. Automatic preprocessing/ stacking/background-elimination with reasonable defaults.
|
|
16
16
|
* Multi-session - by default. So your workflows can stack from multiple nights (and still use the correct flats etc...).
|
|
17
17
|
* Shareable - you can share/use recipes for image preprocessing flows. The eventual goal is a transition away from opaque burried scripts. Recipes can come from multiple repos and you or other orgs can host recipes.
|
|
@@ -56,7 +56,7 @@ If you are interested in alpha-testing we ❤️ you. This README should have e
|
|
|
56
56
|
* Automatically performs **complete** preprocessing on OSC (broadband, narrowband, or dual Duo filter). i.e., gives you 'seestar-level' auto-preprocessing, so you only need to do the (optional) custom post-processing.
|
|
57
57
|
* Uses Siril recipes for its pre-processing operations (support for Pixinsight-based recipes will probably be coming at some point...).
|
|
58
58
|
|
|
59
|
-
### From the alpha 2 (0.2.0) release (2025/12/12
|
|
59
|
+
### From the alpha 2 (0.2.0) release (2025/12/12)
|
|
60
60
|
* Include a stretched 'thumbnail'(FIXME/link/example/thumbnail) jpg proof-of-concept render in the output directories
|
|
61
61
|
* Generates a per-target [report/config file](doc/toml/example/processed-repo/example-m20.toml) which can be customized if the detected defaults or preprocessing are not what you want.
|
|
62
62
|
* '[Recipes](https://github.com/geeksville/starbash-recipes)' are now user-editable - provides repeatable/human-[readable](doc/toml/example/processed-repo/example-m20.toml)/shareable descriptions of all processing steps. [example Siril OSC stacking](https://github.com/geeksville/starbash-recipes/blob/6d255f48591c4991ddc1168ffa5c38050a500771/osc/osc_simple.toml), [example Graxpert BGE](https://github.com/geeksville/starbash-recipes/blob/6d255f48591c4991ddc1168ffa5c38050a500771/graxpert/background.toml), [example thumbnail generation](https://github.com/geeksville/starbash-recipes/blob/6d255f48591c4991ddc1168ffa5c38050a500771/common/thumbnail.toml)...
|
|
@@ -219,6 +219,7 @@ Starbash is a tool agnostic workflow manager. But it wouldn't be possible witho
|
|
|
219
219
|
* [@codegistics](https://github.com/codegistics) for kindly donating Dwarf3 test data and invaluable debugging assistance.
|
|
220
220
|
* The developers of Siril and Graxpert - which are wonderful tools.
|
|
221
221
|
* The [doit](https://pydoit.org/) an **amazing** automation building tool, which substantially simiplified this tool's development.
|
|
222
|
+
* Any parts of the user interface that look good are probably due to the awesome [rich](https://github.com/Textualize/rich) library.
|
|
222
223
|
|
|
223
224
|
## Development
|
|
224
225
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "starbash"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.4"
|
|
4
4
|
description = "A tool for automating/standardizing/sharing astrophotography workflows."
|
|
5
5
|
authors = ["Kevin Hester <kevinh@geeksville.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -25,10 +25,16 @@ sentry-sdk = "^2.42.1"
|
|
|
25
25
|
update-checker = "^0.18.0"
|
|
26
26
|
requests-cache = "^1.2.1"
|
|
27
27
|
doit = "^0.36.0"
|
|
28
|
+
textual = "^6.8.0"
|
|
28
29
|
|
|
29
30
|
# Note: to use graxpert from the local tree rather than pypi use:
|
|
30
31
|
# graxpert = {path = "GraXpert", extras = ["cpuonly"], develop = true}
|
|
31
|
-
graxpert = {version = "^3.2.
|
|
32
|
+
graxpert = {version = "^3.2.0a2", extras = ["cpuonly"]}
|
|
33
|
+
|
|
34
|
+
# The following two libs are only needed for the experiment of running siril scripts directly...
|
|
35
|
+
# experimenting with siril-scripts/processing/VeraLux_HyperMetric_Stretch.py
|
|
36
|
+
#numpy = "2.2.6"
|
|
37
|
+
#pyqt6 = "^6.10.1"
|
|
32
38
|
|
|
33
39
|
[tool.poetry.group.dev.dependencies]
|
|
34
40
|
pytest = ">=8.4.2,<9.0.0"
|
|
@@ -37,6 +43,7 @@ pytest-xdist = "^3.8.0"
|
|
|
37
43
|
pre-commit = "^4.3.0"
|
|
38
44
|
ruff = "^0.14.4"
|
|
39
45
|
basedpyright = "^1.33.0"
|
|
46
|
+
textual-dev = "^1.8.0"
|
|
40
47
|
|
|
41
48
|
# We don't currently use this text based sql client
|
|
42
49
|
# harlequin = "^2.4.1"
|
|
@@ -56,7 +63,7 @@ markers = [
|
|
|
56
63
|
# --tb=short shows shorter tracebacks
|
|
57
64
|
# -rA shows summary of all test outcomes at end
|
|
58
65
|
# --ignore excludes third-party test directories
|
|
59
|
-
addopts = "-m 'not slow and not integration' -n auto --tb=short -rA --ignore=GraXpert"
|
|
66
|
+
addopts = "-m 'not slow and not integration' -n auto --tb=short -rA --ignore=GraXpert --ignore=textual --ignore=starbash-recipes"
|
|
60
67
|
|
|
61
68
|
# Note: To limit captured output further, you can:
|
|
62
69
|
# 1. Use --tb=line for minimal tracebacks
|
|
@@ -88,7 +95,24 @@ line-length = 100
|
|
|
88
95
|
target-version = "py312"
|
|
89
96
|
|
|
90
97
|
# Exclude a variety of commonly ignored directories
|
|
91
|
-
exclude = [".git", ".venv", "__pycache__", "build", "dist"]
|
|
98
|
+
exclude = [".git", ".venv", "__pycache__", "build", "dist", "reference", "siril-scripts"]
|
|
99
|
+
|
|
100
|
+
[tool.ruff.format]
|
|
101
|
+
# Only format files in src/ and tests/ - exclude everything else
|
|
102
|
+
exclude = [
|
|
103
|
+
"doc/**",
|
|
104
|
+
"examples/**",
|
|
105
|
+
"siril-scripts/**",
|
|
106
|
+
"htmlcov/**",
|
|
107
|
+
"images/**",
|
|
108
|
+
"reference/**",
|
|
109
|
+
"starbash-recipes/**",
|
|
110
|
+
"test-data/**",
|
|
111
|
+
"GraXpert/**",
|
|
112
|
+
"textual/**",
|
|
113
|
+
"bin/**",
|
|
114
|
+
"*.py" # Exclude root-level Python files
|
|
115
|
+
]
|
|
92
116
|
|
|
93
117
|
[tool.ruff.lint]
|
|
94
118
|
# Enable these rule sets
|
|
@@ -505,7 +505,12 @@ class Repo:
|
|
|
505
505
|
"""
|
|
506
506
|
# Construct the full URL by joining the base URL with the filepath
|
|
507
507
|
if filepath:
|
|
508
|
-
|
|
508
|
+
# If the URL points to a .toml file, strip the filename to get the directory
|
|
509
|
+
base_url = self.url
|
|
510
|
+
if self._is_direct_toml_file():
|
|
511
|
+
# Strip the .toml filename to get the parent directory URL
|
|
512
|
+
base_url = base_url.rsplit("/", 1)[0]
|
|
513
|
+
url = base_url.rstrip("/") + "/" + filepath.lstrip("/")
|
|
509
514
|
else:
|
|
510
515
|
url = self.url
|
|
511
516
|
|
|
@@ -7,7 +7,7 @@ from rich.console import Console
|
|
|
7
7
|
|
|
8
8
|
# Common type aliases for clarity
|
|
9
9
|
type StageDict = dict[str, Any] # a processing stage definition from our toml
|
|
10
|
-
type InputDef = dict[
|
|
10
|
+
type InputDef = dict[Any, Any] # an input definition within a stage
|
|
11
11
|
type OutputDef = dict[str, Any] # an output definition within a stage
|
|
12
12
|
type RequireDef = dict[str, Any] # a requires definition within an input
|
|
13
13
|
type Metadata = dict[str, Any] # image metadata dictionary
|
|
@@ -47,9 +47,9 @@ def analytics_setup(allowed: bool = False, user_email: str | None = None) -> Non
|
|
|
47
47
|
], # This line removes the aggressive unhandled exception catcher
|
|
48
48
|
integrations=[
|
|
49
49
|
LoggingIntegration(
|
|
50
|
-
level=starbash.log_filter_level, # Capture INFO and above as breadcrumbs
|
|
50
|
+
level=starbash.log_filter_level, # Capture INFO and above as breadcrumbs (used for exception reports)
|
|
51
51
|
event_level=None, # Don't automatically convert error messages to sentry events
|
|
52
|
-
sentry_logs_level=
|
|
52
|
+
sentry_logs_level=logging.INFO, # Capture INFO and above as logs (general usage)
|
|
53
53
|
),
|
|
54
54
|
],
|
|
55
55
|
)
|
|
@@ -85,7 +85,7 @@ def is_development_environment() -> bool:
|
|
|
85
85
|
return False
|
|
86
86
|
|
|
87
87
|
|
|
88
|
-
def analytics_exception(exc:
|
|
88
|
+
def analytics_exception(exc: BaseException) -> bool:
|
|
89
89
|
"""Report an exception to the analytics service, if enabled.
|
|
90
90
|
return True to suppress exception propagation/log messages"""
|
|
91
91
|
|
|
@@ -3,6 +3,7 @@ import logging
|
|
|
3
3
|
import sys
|
|
4
4
|
from importlib.metadata import version
|
|
5
5
|
from pathlib import Path
|
|
6
|
+
from sqlite3 import OperationalError
|
|
6
7
|
from string import Template
|
|
7
8
|
from typing import Any
|
|
8
9
|
|
|
@@ -36,7 +37,7 @@ from starbash.database import (
|
|
|
36
37
|
get_column_name,
|
|
37
38
|
)
|
|
38
39
|
from starbash.dwarf3 import extend_dwarf3_headers
|
|
39
|
-
from starbash.exception import UserHandledError, raise_missing_repo
|
|
40
|
+
from starbash.exception import NonSoftwareError, UserHandledError, raise_missing_repo
|
|
40
41
|
from starbash.linux import linux_init
|
|
41
42
|
from starbash.os import symlink_or_copy
|
|
42
43
|
from starbash.paths import get_user_config_dir, get_user_config_path
|
|
@@ -121,6 +122,28 @@ def copy_images_to_dir(images: list[ImageRow], output_dir: Path) -> None:
|
|
|
121
122
|
console.print(f" [red]Errors: {error_count} files[/red]")
|
|
122
123
|
|
|
123
124
|
|
|
125
|
+
def remap_expected_errors(exc: BaseException | None) -> BaseException | None:
|
|
126
|
+
"""Remap certain expected exceptions to UserHandledError for consistent handling."""
|
|
127
|
+
|
|
128
|
+
if exc is None:
|
|
129
|
+
return None
|
|
130
|
+
|
|
131
|
+
# Some OSErrors definitely don't indicate a code level bug...
|
|
132
|
+
expected_errors = [ "Read-only file system" ]
|
|
133
|
+
|
|
134
|
+
if isinstance(exc, OperationalError):
|
|
135
|
+
return NonSoftwareError(f"[red]Database IO error:[/red] {exc}")
|
|
136
|
+
elif isinstance(exc, FileNotFoundError):
|
|
137
|
+
return NonSoftwareError(f"[red]OS error:[/red] {exc}")
|
|
138
|
+
elif isinstance(exc, OSError):
|
|
139
|
+
exc_str = str(exc)
|
|
140
|
+
if any(err in exc_str for err in expected_errors):
|
|
141
|
+
return NonSoftwareError(f"[red]OS error:[/red] {exc}")
|
|
142
|
+
# Add more remappings as needed
|
|
143
|
+
|
|
144
|
+
return exc
|
|
145
|
+
|
|
146
|
+
|
|
124
147
|
class Starbash:
|
|
125
148
|
"""The main Starbash application class."""
|
|
126
149
|
|
|
@@ -161,7 +184,8 @@ class Starbash:
|
|
|
161
184
|
self._init_repos()
|
|
162
185
|
self._init_analytics(cmd) # after init repos so we have user prefs
|
|
163
186
|
|
|
164
|
-
|
|
187
|
+
app_version = version("starbash")
|
|
188
|
+
logging.info(f"🚀 Starbash {app_version} starting!")
|
|
165
189
|
|
|
166
190
|
check_version()
|
|
167
191
|
self._init_aliases()
|
|
@@ -274,12 +298,18 @@ class Starbash:
|
|
|
274
298
|
from starbash import _is_test_env
|
|
275
299
|
from starbash.exception import UserHandledError
|
|
276
300
|
|
|
301
|
+
exc = remap_expected_errors(exc)
|
|
302
|
+
|
|
277
303
|
handled = False
|
|
278
304
|
# Don't suppress typer.Exit - it's used for controlled exit codes
|
|
279
305
|
if exc and not isinstance(exc, typer.Exit) and not isinstance(exc, KeyboardInterrupt):
|
|
280
|
-
# For
|
|
281
|
-
if isinstance(exc,
|
|
282
|
-
|
|
306
|
+
# For errors we expect, just print error message and exit cleanly
|
|
307
|
+
if isinstance(exc, NonSoftwareError):
|
|
308
|
+
# For UserHandledError, call ask_user_handled() and don't suppress
|
|
309
|
+
if isinstance(exc, UserHandledError):
|
|
310
|
+
exc.ask_user_handled()
|
|
311
|
+
else:
|
|
312
|
+
starbash.console.print(exc)
|
|
283
313
|
self.close()
|
|
284
314
|
# Raise typer.Exit to ensure proper exit code
|
|
285
315
|
raise typer.Exit(code=1)
|
|
@@ -288,7 +318,7 @@ class Starbash:
|
|
|
288
318
|
self.close()
|
|
289
319
|
if not handled:
|
|
290
320
|
# Show the full exception for developers
|
|
291
|
-
starbash.console.print_exception(show_locals=
|
|
321
|
+
starbash.console.print_exception(show_locals=False) # Locals are too verbose
|
|
292
322
|
|
|
293
323
|
# In test environments, let exceptions propagate naturally for better test diagnostics
|
|
294
324
|
if not _is_test_env:
|
|
@@ -229,7 +229,7 @@ def auto(
|
|
|
229
229
|
|
|
230
230
|
if session_num is not None:
|
|
231
231
|
console.print(
|
|
232
|
-
f"[
|
|
232
|
+
f"[red]Session number base filtering not yet implemented: {session_num}...[/red]"
|
|
233
233
|
)
|
|
234
234
|
else:
|
|
235
235
|
console.print("[yellow]Auto-processing all selected sessions...[/yellow]")
|
|
@@ -178,11 +178,9 @@ def do_reinit(sb: Starbash) -> None:
|
|
|
178
178
|
Recommended next steps (to get your first auto-generated images):
|
|
179
179
|
1. Add your source raw image repositories (starbash will only READ from these):
|
|
180
180
|
[cyan]sb repo add /path/to/your/raw_images[/cyan]
|
|
181
|
-
2.
|
|
182
|
-
[cyan]sb process masters[/cyan]
|
|
183
|
-
3. Process your images using automated workflows:
|
|
181
|
+
2. Process your images using automated workflows:
|
|
184
182
|
[cyan]sb process auto[/cyan]
|
|
185
|
-
|
|
183
|
+
3. (Highly recommended) Tell your shell to auto-complete starbash commands:
|
|
186
184
|
[cyan]sb shell-complete --install-completion[/cyan]
|
|
187
185
|
|
|
188
186
|
This project is currently very 'alpha' but we are eager to have it work for you.
|
|
@@ -252,11 +252,10 @@ class ToolAction(BaseAction):
|
|
|
252
252
|
self.result = self.tool.run(
|
|
253
253
|
self.commands, context=context, cwd=self.cwd, log_out=logfile, **self.parameters
|
|
254
254
|
)
|
|
255
|
-
except
|
|
255
|
+
except Exception as e:
|
|
256
256
|
# We pass back any exceptions in task.meta - so that our ConsoleReporter can pick them up (doit normally strips exceptions)
|
|
257
257
|
self.task.meta["exception"] = e
|
|
258
258
|
return TaskFailed("tool failed")
|
|
259
|
-
# raise e # Let doit do its normal exception handling though
|
|
260
259
|
|
|
261
260
|
self.values = {} # doit requires this attribute to be set
|
|
262
261
|
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
from typing import Any, NoReturn
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
class
|
|
4
|
+
class NonSoftwareError(Exception):
|
|
5
|
+
"""An error outside the control of the software, exit the app with an error code, but no need to
|
|
6
|
+
report it as a software bug.
|
|
7
|
+
|
|
8
|
+
Used for OS errors etc after we've determined we can't do anything more useful."""
|
|
9
|
+
|
|
10
|
+
def __rich__(self) -> Any:
|
|
11
|
+
return self.__str__() # At least this is something readable...
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class UserHandledError(NonSoftwareError):
|
|
5
15
|
"""An exception that terminates processing of the current file, but we want to help the user fix the problem."""
|
|
6
16
|
|
|
7
17
|
def ask_user_handled(self) -> bool:
|
|
@@ -14,9 +24,6 @@ class UserHandledError(ValueError):
|
|
|
14
24
|
console.print(f"Error: {self}")
|
|
15
25
|
return False
|
|
16
26
|
|
|
17
|
-
def __rich__(self) -> Any:
|
|
18
|
-
return self.__str__() # At least this is something readable...
|
|
19
|
-
|
|
20
27
|
|
|
21
28
|
def raise_missing_repo(kind: str) -> NoReturn:
|
|
22
29
|
"""Raise a UserHandledError indicating that a repository of the given kind is missing."""
|
|
@@ -64,8 +64,13 @@ def get_user_data_dir() -> Path:
|
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
def get_user_cache_dir() -> Path:
|
|
67
|
-
"""Get the user cache directory. Returns test override if set, otherwise the real user directory."""
|
|
68
|
-
|
|
67
|
+
"""Get the user cache directory. Returns test override if set, otherwise checks STARBASH_CACHE_DIR env var, otherwise the real user directory."""
|
|
68
|
+
if _override_cache_dir is not None:
|
|
69
|
+
dir_to_use = _override_cache_dir
|
|
70
|
+
elif env_cache_dir := os.getenv("STARBASH_CACHE_DIR"):
|
|
71
|
+
dir_to_use = Path(env_cache_dir)
|
|
72
|
+
else:
|
|
73
|
+
dir_to_use = cache_dir
|
|
69
74
|
os.makedirs(dir_to_use, exist_ok=True)
|
|
70
75
|
return dir_to_use
|
|
71
76
|
|
|
@@ -10,117 +10,17 @@ import tomlkit
|
|
|
10
10
|
|
|
11
11
|
from repo import Repo, repo_suffix
|
|
12
12
|
from starbash import StageDict, to_shortdate
|
|
13
|
-
from starbash.
|
|
14
|
-
from starbash.doit_types import TaskDict, cleanup_old_contexts, get_processing_dir
|
|
13
|
+
from starbash.doit_types import cleanup_old_contexts, get_processing_dir
|
|
15
14
|
from starbash.parameters import ParameterStore
|
|
16
15
|
from starbash.processing_like import ProcessingLike
|
|
17
16
|
from starbash.safety import get_safe
|
|
18
|
-
from starbash.toml import
|
|
17
|
+
from starbash.toml import toml_from_template
|
|
19
18
|
|
|
20
19
|
__all__ = [
|
|
21
20
|
"ProcessedTarget",
|
|
22
21
|
]
|
|
23
22
|
|
|
24
23
|
|
|
25
|
-
def stage_with_comment(stage: StageDict) -> CommentedString:
|
|
26
|
-
"""Create a CommentedString for the given stage."""
|
|
27
|
-
name = stage.get("name", "unnamed_stage")
|
|
28
|
-
description = stage.get("description", None)
|
|
29
|
-
return CommentedString(value=name, comment=description)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def set_used(self: dict, used_stages: list[StageDict]) -> None:
|
|
33
|
-
"""Set the used lists for the given section."""
|
|
34
|
-
name = "stages"
|
|
35
|
-
used = [stage_with_comment(s) for s in used_stages]
|
|
36
|
-
node = self.setdefault(name, {})
|
|
37
|
-
node["used"] = toml_from_list(used)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def set_excluded(self: dict, stages_to_exclude: list[StageDict]) -> None:
|
|
41
|
-
"""Set the excluded lists for the given section."""
|
|
42
|
-
name = "stages"
|
|
43
|
-
excluded = [stage_with_comment(s) for s in stages_to_exclude]
|
|
44
|
-
|
|
45
|
-
node = self.setdefault(name, {})
|
|
46
|
-
node["excluded"] = toml_from_list(excluded)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def get_from_toml(self: dict, key_name: str) -> list[str]:
|
|
50
|
-
"""Any consumers of this function probably just want the raw string (key_name is usually excluded or used)"""
|
|
51
|
-
dict_name = "stages"
|
|
52
|
-
node = self.setdefault(dict_name, {})
|
|
53
|
-
excluded: list[CommentedString] = node.get(key_name, [])
|
|
54
|
-
return [a.value for a in excluded]
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def task_to_stage(task: TaskDict) -> StageDict:
|
|
58
|
-
"""Extract the stage from the given task's context."""
|
|
59
|
-
return task["meta"]["stage"]
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def task_to_session(task: TaskDict) -> SessionRow | None:
|
|
63
|
-
"""Extract the session from the given task's context."""
|
|
64
|
-
context = task["meta"]["context"]
|
|
65
|
-
session = context.get("session")
|
|
66
|
-
return session
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def tasks_to_stages(tasks: list[TaskDict]) -> list[StageDict]:
|
|
70
|
-
"""Extract unique stages from the given list of tasks, sorted by priority."""
|
|
71
|
-
stage_dict: dict[str, StageDict] = {}
|
|
72
|
-
for task in tasks:
|
|
73
|
-
stage = task["meta"]["stage"]
|
|
74
|
-
stage_dict[stage["name"]] = stage
|
|
75
|
-
|
|
76
|
-
# Sort stages by priority (if priority not present assume 0), higher priority first
|
|
77
|
-
stages = sorted(stage_dict.values(), key=lambda s: s.get("priority", 0), reverse=True)
|
|
78
|
-
logging.debug(f"Stages in priority order: {[s.get('name') for s in stages]}")
|
|
79
|
-
return stages
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def set_used_stages_from_tasks(tasks: list[dict]) -> None:
|
|
83
|
-
"""Given a list of tasks, set the used stages in each session touched by those tasks."""
|
|
84
|
-
|
|
85
|
-
# Inside each session we touched, collect a list of used stages (initially as a list of strings but then in the final)
|
|
86
|
-
# cleanup converted into toml lists with set_used.
|
|
87
|
-
# We rely on the fact that a single session row instance is shared between all tasks for that session.
|
|
88
|
-
|
|
89
|
-
if not tasks:
|
|
90
|
-
return
|
|
91
|
-
|
|
92
|
-
typ_task = tasks[0]
|
|
93
|
-
pt: ProcessedTarget | None = typ_task["meta"]["processed_target"]
|
|
94
|
-
assert pt, "ProcessedTarget must be set in Processing for sessionless tasks"
|
|
95
|
-
|
|
96
|
-
# step 1: clear our temp lists
|
|
97
|
-
default_stages: list[StageDict] = []
|
|
98
|
-
for task in tasks:
|
|
99
|
-
session = task_to_session(task)
|
|
100
|
-
if session:
|
|
101
|
-
session["_temp_used_stages"] = []
|
|
102
|
-
|
|
103
|
-
# step 2: collect used stages
|
|
104
|
-
for task in tasks:
|
|
105
|
-
stage = task_to_stage(task)
|
|
106
|
-
session = task_to_session(task)
|
|
107
|
-
used = session["_temp_used_stages"] if session else default_stages
|
|
108
|
-
if stage not in used:
|
|
109
|
-
used.append(stage)
|
|
110
|
-
|
|
111
|
-
# step 3: commit used stages to toml (and remove temp lists)
|
|
112
|
-
for task in tasks:
|
|
113
|
-
session = task_to_session(task)
|
|
114
|
-
if session:
|
|
115
|
-
used_stages: list[StageDict] = session.pop("_temp_used_stages", [])
|
|
116
|
-
if used_stages:
|
|
117
|
-
set_used(session, used_stages)
|
|
118
|
-
|
|
119
|
-
# Commit our default used stages too
|
|
120
|
-
if default_stages:
|
|
121
|
-
set_used(pt.default_stages, default_stages)
|
|
122
|
-
|
|
123
|
-
|
|
124
24
|
class ProcessedTarget:
|
|
125
25
|
"""The repo file based config for a single processed target.
|
|
126
26
|
|
|
@@ -223,6 +123,8 @@ class ProcessedTarget:
|
|
|
223
123
|
|
|
224
124
|
def _set_default_stages(self) -> None:
|
|
225
125
|
"""If we have newly discovered stages which should be excluded by default, add them now."""
|
|
126
|
+
from starbash.stage_utils import get_from_toml, set_excluded
|
|
127
|
+
|
|
226
128
|
excluded = get_from_toml(self.default_stages, "excluded")
|
|
227
129
|
used: list[str] = get_from_toml(self.default_stages, "used")
|
|
228
130
|
|