Skip to content

Commit 421cc3a

Browse files
authored
πŸ”€ Merge pull request #156 from rohangirishrao/save-script-params
Rework and generalize `save_script_parameters`
2 parents e994252 + 3ec77db commit 421cc3a

3 files changed

Lines changed: 95 additions & 20 deletions

File tree

β€ŽCHANGELOG.mdβ€Ž

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ multiple modules. For detailed information, please refer to the
7171
* `imcflibs.imagej.misc.run_imarisconvert`:
7272
* Now allows to select an output folder.
7373
* Adds pixel calibration to the converted file.
74+
* `imcflibs.imagej.misc.save_script_parameters` skips only parameters whose
75+
whole name is in the skip list (`fusion_format` was skipped because it
76+
contains `RM`).
7477
* `imcflibs.imagej.misc.subtract_images` now validates the slice count is equal
7578
for both images.
7679
* `imcflibs.imagej.misc.save_image_with_extension`: fix issue with java path

β€Žsrc/imcflibs/imagej/misc.pyβ€Ž

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -784,16 +784,17 @@ def _is_password_style(item): # pragma: no cover (jython)
784784
return WidgetStyle.isStyle(item, TextWidget.PASSWORD_STYLE)
785785

786786

787-
def save_script_parameters(
788-
script_globals, destination, save_file_name="script_parameters.txt"
789-
):
787+
def save_script_parameters(script_globals, destination="", overwrite=False):
790788
"""Save all Fiji script parameters to a text file.
791789
792790
Record all input parameters defined in the Fiji script header (e.g.
793791
`#@ String`) to a text file such that they can be stored e.g. next to the
794792
input data and the analysis results in order to document how a specific
795793
processing run was executed.
796794
795+
The file name is generated automatically following the pattern
796+
``<script_name>_run-<timestamp>_params.txt``.
797+
797798
The following parameters are excluded:
798799
799800
- Parameters explicitly declared with `style="password"`.
@@ -808,30 +809,49 @@ def save_script_parameters(
808809
script_globals : dict
809810
The globals dictionary from the running Fiji instance. Must be passed
810811
explicitly as `globals()` by the calling code.
811-
destination : str
812-
Directory where the script parameters file will be saved.
813-
save_file_name : str, optional
814-
Name of the script parameters file, by default "script_parameters.txt".
812+
destination : str, optional
813+
Directory where the script parameters file will be saved. If empty
814+
(the default), parameter saving is skipped.
815+
overwrite : bool, optional
816+
If True, use a fixed filename ``<script_name>_params.txt`` that gets
817+
overwritten on each run. If False (the default), a timestamp is
818+
included in the filename to preserve parameters from each run.
815819
816820
Examples
817821
--------
818822
In a Fiji script, you can call this function as follows to save the parameters:
819823
820824
>>> save_script_parameters(script_globals=globals(), destination="/data")
821-
Saved script parameters to: /data/script_parameters.txt
825+
Saved 2 script parameters to: /data/example1_run-2026-04-10--10-41_params.txt
822826
"""
827+
if destination == "":
828+
timed_log("No destination provided for saving script parameters - skipping.")
829+
return
830+
823831
try:
824832
module = script_globals.get("org.scijava.script.ScriptModule")
825-
# Access script metadata and inputs
826833
script_info = module.getInfo()
827834
inputs = module.getInputs()
828835
except:
829836
timed_log("ScriptModule inspection failed - skipping saving of parameters.")
830837
return
831838

832-
# NOTE: the two parameters are intentionally kept separate for (1) consistency
833-
# reasons with other scripts and (b) as this allows for easier modification of just
834-
# the output file e.g. in subsequent runs.
839+
script_location = script_info.getLocation()
840+
if script_location:
841+
# getLocation() returns "file:/path/to/script.py"
842+
# strip the "file:" prefix to get the actual path
843+
if script_location.startswith("file:"):
844+
script_location = script_location[5:]
845+
script_name = os.path.splitext(os.path.basename(script_location))[0]
846+
else:
847+
script_name = "script"
848+
849+
if overwrite:
850+
save_file_name = "%s_params.txt" % script_name
851+
else:
852+
timestamp = time.strftime("%Y-%m-%d--%H-%M")
853+
save_file_name = "%s_run-%s_params.txt" % (script_name, timestamp)
854+
835855
destination = str(destination)
836856
out_path = os.path.join(destination, save_file_name)
837857

@@ -843,8 +863,9 @@ def save_script_parameters(
843863
for item in script_info.inputs():
844864
key = item.getName()
845865

846-
# Skip if any keys are in the skip list
847-
if any(skip in key.upper() for skip in skip_keys):
866+
# Skip if the key is in the skip list (whole name, not a substring:
867+
# e.g. "FUSION_FORMAT" contains "RM")
868+
if key.upper() in skip_keys:
848869
log.info("Skipping parameter from skip-list: %s", key)
849870
skipped += 1
850871
continue
@@ -855,7 +876,6 @@ def save_script_parameters(
855876
passwords += 1
856877
continue
857878

858-
# TODO: discuss if this approach is fine within Fiji/Jython
859879
try:
860880
val = inputs.get(key)
861881
if val is None: # required for testing in CPython

β€Žtests/test_misc.pyβ€Ž

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Tests for `imcflibs.imagej.misc` utility functions."""
22

33
import logging
4+
import os
45

56
from org.scijava.script import ScriptInfo, ScriptModule
67

@@ -9,15 +10,22 @@
910
from imcflibs.imagej.misc import bytes_to_human_readable
1011
from imcflibs.imagej.misc import save_script_parameters
1112

12-
1313
PASSWORD_ITEMS = ["OMERO_PASSWD"]
1414

1515

16-
def test_save_script_parameters_fail(caplog):
16+
def test_save_script_parameters_no_destination(caplog):
17+
"""Tests save_script_parameters skips saving without a destination."""
18+
caplog.clear()
19+
20+
save_script_parameters(script_globals=None)
21+
assert "No destination provided" in caplog.messages[0]
22+
23+
24+
def test_save_script_parameters_fail(tmp_path, caplog):
1725
"""Tests save_script_parameters with an invalid script_globals object."""
1826
caplog.clear()
1927

20-
save_script_parameters(script_globals=None, destination="")
28+
save_script_parameters(script_globals=None, destination=tmp_path)
2129
assert "ScriptModule inspection failed" in caplog.messages[0]
2230

2331

@@ -38,19 +46,63 @@ def _is_password_style(item):
3846
input_names=["AAA", "BBB", "OMERO_PASSWD", "SJLOG", "NOT_THERE"],
3947
inputs={"AAA": "aaa", "BBB": "bbb", "OMERO_PASSWD": "ultra-secret"},
4048
)
49+
script_module.getInfo().location = "file:/some/path/example1.py"
4150
script_globals = {"org.scijava.script.ScriptModule": script_module}
42-
save_script_parameters(script_globals, destination=base)
51+
save_script_parameters(script_globals, destination=base, overwrite=True)
4352
assert "Skipping parameter from skip-list" in caplog.text
4453
assert "Skipping password-style parameter" in caplog.text
4554
assert "Unable to fetch value for parameter: NOT_THERE" in caplog.text
4655
assert "Saved 2 parameters (skipped 1 password-style and 1 others)." in caplog.text
4756
assert "Saved 2 script parameters to" in caplog.text
4857

49-
with open(str(base) + "/script_parameters.txt", "r") as f:
58+
with open(str(base) + "/example1_params.txt", "r") as f:
5059
contents = f.read()
5160
assert contents == "AAA: aaa\nBBB: bbb\n"
5261

5362

63+
def test_save_script_parameters_timestamped(tmp_path, monkeypatch):
64+
"""Tests save_script_parameters default naming without a script location."""
65+
66+
def _is_password_style(item):
67+
return False
68+
69+
monkeypatch.setattr(imcflibs.imagej.misc, "_is_password_style", _is_password_style)
70+
71+
script_module = ScriptModule(input_names=["AAA"], inputs={"AAA": "aaa"})
72+
script_globals = {"org.scijava.script.ScriptModule": script_module}
73+
save_script_parameters(script_globals, destination=tmp_path)
74+
75+
# the folder should now contain exactly one file
76+
files = os.listdir(str(tmp_path))
77+
assert len(files) == 1
78+
79+
# its name is "script_run-<timestamp>_params.txt"
80+
file_name = files[0]
81+
assert file_name.startswith("script_run-")
82+
assert file_name.endswith("_params.txt")
83+
84+
with open(os.path.join(str(tmp_path), file_name), "r") as f:
85+
assert f.read() == "AAA: aaa\n"
86+
87+
88+
def test_save_script_parameters_skip_whole_names(tmp_path, monkeypatch, caplog):
89+
"""Only whole names are skipped, case insensitive, not substrings."""
90+
caplog.set_level(logging.DEBUG)
91+
caplog.clear()
92+
monkeypatch.setattr(imcflibs.imagej.misc, "_is_password_style", lambda item: False)
93+
94+
script_module = ScriptModule(
95+
input_names=["rm", "fusion_format", "commander"],
96+
inputs={"rm": "roimanager", "fusion_format": "TIFF", "commander": "x"},
97+
)
98+
script_globals = {"org.scijava.script.ScriptModule": script_module}
99+
save_script_parameters(script_globals, destination=tmp_path, overwrite=True)
100+
101+
with open(str(tmp_path) + "/script_params.txt", "r") as f:
102+
contents = f.read()
103+
assert contents == "fusion_format: TIFF\ncommander: x\n"
104+
105+
54106
def test_bytes_to_human_readable_simple():
55107
"""Ensure common sizes are formatted into human-readable strings."""
56108
assert bytes_to_human_readable(500) == "500.0 bytes"

0 commit comments

Comments
Β (0)