From 2b4e7e25ae7e2c248a21a2e4ccea0014c7e22d94 Mon Sep 17 00:00:00 2001 From: Daniel Kohler <11864045+ddkohler@users.noreply.github.com> Date: Wed, 2 Sep 2026 11:12:19 -0500 Subject: [PATCH 1/6] rough attempt --- WrightTools/data/_solis.py | 94 +++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 37 deletions(-) diff --git a/WrightTools/data/_solis.py b/WrightTools/data/_solis.py index ae0018cc..459152f1 100644 --- a/WrightTools/data/_solis.py +++ b/WrightTools/data/_solis.py @@ -7,7 +7,7 @@ import pathlib import time import warnings - +import string import numpy as np from ._data import Data @@ -59,7 +59,6 @@ def from_Solis(filepath, name=None, parent=None, verbose=True) -> Data: """ # parse filepath - filestr = os.fspath(filepath) filepath = pathlib.Path(filepath) if not ".asc" in filepath.suffixes: @@ -69,49 +68,19 @@ def from_Solis(filepath, name=None, parent=None, verbose=True) -> Data: name = filepath.name.split(".")[0] # create data ds = DataSource(None) - f = ds.open(filestr, "rt") + f = ds.open(str(filepath), "rt") axis0 = [] arr = [] attrs = {} - line0 = f.readline().strip()[:-1] - line0 = [float(x) for x in line0.split(",")] # TODO: robust to space, tab, comma - axis0.append(line0.pop(0)) - arr.append(line0) + attrs, pos = parse_metadata(f) - def get_frames(f, arr, axis0): - axis0_written = False - while True: - line = f.readline().strip()[:-1] - if len(line) == 0: - break - else: - line = [float(x) for x in line.split(",")] - # signature of new frames is restart of axis0 - if not axis0_written and (line[0] == axis0[0]): - axis0_written = True - if axis0_written: - line.pop(0) - else: - axis0.append(line.pop(0)) - arr.append(line) - return arr, axis0 + f.seek(pos) arr, axis0 = get_frames(f, arr, axis0) nframes = len(arr) // len(axis0) - i = 0 - while i < 3: - line = f.readline().strip() - if len(line) == 0: - i += 1 - else: - try: - key, val = line.split(":", 1) - except ValueError: - pass - else: - attrs[key.strip()] = val.strip() + attrs.update(parse_metadata(f)[0]) f.close() @@ -126,7 +95,7 @@ def get_frames(f, arr, axis0): f"{filepath.name} has no 'Date and Time' field: using file modified time instead: {created}" ) - kwargs = {"name": name, "kind": "Solis", "source": filestr, "created": created} + kwargs = {"name": name, "kind": "Solis", "source": filepath.name, "created": created} if parent is None: data = Data(**kwargs) else: @@ -184,3 +153,54 @@ def get_frames(f, arr, axis0): print(" axes: {0}".format(data.axis_names)) print(" shape: {0}".format(data.shape)) return data + + + +def get_frames(f, arr, axis0): + axis0_written = False + line0 = f.readline().strip()[:-1] + line0 = [float(x) for x in line0.split(",")] # TODO: robust to space, tab, comma + axis0.append(line0.pop(0)) + arr.append(line0) + + while True: + line = f.readline().strip()[:-1] + # data ends at a blank line (potentially EOF) + if not line: + break + else: + line = [float(x) for x in line.split(",")] + # signature of new frames is restart of axis0 + if not axis0_written and (line[0] == axis0[0]): + axis0_written = True + if axis0_written: + line.pop(0) + else: + axis0.append(line.pop(0)) + arr.append(line) + + return arr, axis0 + + +def parse_metadata(f): + attrs = {} + + # terminate parsing either + # when EOF is reached + # when numeric data is reached + pos = f.tell() + while True: + pos = f.tell() + line = f.readline() + if (not line) or line[0].isdigit(): # EOF or numeric data + break + line = line.strip()[:-1] + try: + key, val = line.split(":", 1) + except ValueError: + print(f"could not parse line {line}") + else: + attrs[key.strip()] = val.strip() + + return attrs, pos + From 81582b2a3b239e1ff19609847c88162d31babba6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2026 16:14:08 +0000 Subject: [PATCH 2/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- WrightTools/data/_solis.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/WrightTools/data/_solis.py b/WrightTools/data/_solis.py index 459152f1..29650075 100644 --- a/WrightTools/data/_solis.py +++ b/WrightTools/data/_solis.py @@ -155,7 +155,6 @@ def from_Solis(filepath, name=None, parent=None, verbose=True) -> Data: return data - def get_frames(f, arr, axis0): axis0_written = False line0 = f.readline().strip()[:-1] @@ -185,7 +184,7 @@ def get_frames(f, arr, axis0): def parse_metadata(f): attrs = {} - # terminate parsing either + # terminate parsing either # when EOF is reached # when numeric data is reached pos = f.tell() @@ -201,6 +200,5 @@ def parse_metadata(f): print(f"could not parse line {line}") else: attrs[key.strip()] = val.strip() - - return attrs, pos + return attrs, pos From 36fbbcb32b5c17e935ef907bdb915962b5cdc945 Mon Sep 17 00:00:00 2001 From: Daniel Kohler <11864045+ddkohler@users.noreply.github.com> Date: Wed, 2 Sep 2026 11:23:45 -0500 Subject: [PATCH 3/6] Update _solis.py --- WrightTools/data/_solis.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/WrightTools/data/_solis.py b/WrightTools/data/_solis.py index 29650075..689a5126 100644 --- a/WrightTools/data/_solis.py +++ b/WrightTools/data/_solis.py @@ -73,14 +73,12 @@ def from_Solis(filepath, name=None, parent=None, verbose=True) -> Data: arr = [] attrs = {} - attrs, pos = parse_metadata(f) - - f.seek(pos) + attrs = parse_metadata(f) arr, axis0 = get_frames(f, arr, axis0) nframes = len(arr) // len(axis0) - attrs.update(parse_metadata(f)[0]) + attrs.update(parse_metadata(f)) f.close() @@ -181,18 +179,20 @@ def get_frames(f, arr, axis0): return arr, axis0 -def parse_metadata(f): +def parse_metadata(f) -> dict: + """ + readlines for key value pairs until data or EOF is encountered + when readlines is finished, reverts to last valid line so no data is missed by subsequent readlines + """ attrs = {} - # terminate parsing either - # when EOF is reached - # when numeric data is reached - pos = f.tell() while True: pos = f.tell() line = f.readline() if (not line) or line[0].isdigit(): # EOF or numeric data break + if line[0] not in string.ascii_letters: + continue line = line.strip()[:-1] try: key, val = line.split(":", 1) @@ -201,4 +201,5 @@ def parse_metadata(f): else: attrs[key.strip()] = val.strip() - return attrs, pos + f.seek(pos) + return attrs From 993d1a1cbcb78a4e42e40e19081231f913792352 Mon Sep 17 00:00:00 2001 From: Daniel Kohler <11864045+ddkohler@users.noreply.github.com> Date: Wed, 2 Sep 2026 12:06:42 -0500 Subject: [PATCH 4/6] minor refactor --- WrightTools/data/_solis.py | 11 +++++------ tests/data/from_Solis.py | 2 ++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/WrightTools/data/_solis.py b/WrightTools/data/_solis.py index 689a5126..33fe8cff 100644 --- a/WrightTools/data/_solis.py +++ b/WrightTools/data/_solis.py @@ -68,20 +68,19 @@ def from_Solis(filepath, name=None, parent=None, verbose=True) -> Data: name = filepath.name.split(".")[0] # create data ds = DataSource(None) - f = ds.open(str(filepath), "rt") axis0 = [] arr = [] attrs = {} + # extract the contents of the file + f = ds.open(str(filepath), "rt") attrs = parse_metadata(f) - arr, axis0 = get_frames(f, arr, axis0) nframes = len(arr) // len(axis0) - attrs.update(parse_metadata(f)) - f.close() + # construct the data object try: created = attrs["Date and Time"] # is this UTC? created = time.strptime(created, "%a %b %d %H:%M:%S %Y") @@ -93,7 +92,7 @@ def from_Solis(filepath, name=None, parent=None, verbose=True) -> Data: f"{filepath.name} has no 'Date and Time' field: using file modified time instead: {created}" ) - kwargs = {"name": name, "kind": "Solis", "source": filepath.name, "created": created} + kwargs = {"name": name, "kind": "Solis", "source": str(filepath), "created": created} if parent is None: data = Data(**kwargs) else: @@ -193,7 +192,7 @@ def parse_metadata(f) -> dict: break if line[0] not in string.ascii_letters: continue - line = line.strip()[:-1] + line = line.strip() try: key, val = line.split(":", 1) except ValueError: diff --git a/tests/data/from_Solis.py b/tests/data/from_Solis.py index d8d2bfc2..4d637d3b 100644 --- a/tests/data/from_Solis.py +++ b/tests/data/from_Solis.py @@ -15,6 +15,8 @@ def test_wm_ypos_fluorescence_with_filter(): assert data.shape == (2560, 2160) assert data.axis_expressions == ("wm", "yindex") assert data.units == ("nm", None) + assert data.created.human == '2018-06-30 01:52:35' + data.close() From 7a52d63a1f7b35ab36cabf47a6bb0ce07bd32c43 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2026 17:06:58 +0000 Subject: [PATCH 5/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/data/from_Solis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/data/from_Solis.py b/tests/data/from_Solis.py index 4d637d3b..7f22eab5 100644 --- a/tests/data/from_Solis.py +++ b/tests/data/from_Solis.py @@ -15,7 +15,7 @@ def test_wm_ypos_fluorescence_with_filter(): assert data.shape == (2560, 2160) assert data.axis_expressions == ("wm", "yindex") assert data.units == ("nm", None) - assert data.created.human == '2018-06-30 01:52:35' + assert data.created.human == "2018-06-30 01:52:35" data.close() From e4fa4c3edec227fe40595bac1cf068c84795f6bb Mon Sep 17 00:00:00 2001 From: Daniel Kohler <11864045+ddkohler@users.noreply.github.com> Date: Wed, 2 Sep 2026 17:32:47 -0500 Subject: [PATCH 6/6] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e138410b..776e9446 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/). ## [Unreleased] +### Fixed +- `from_solis`: accepts metadata listed before or after data + ## [3.6.4] ### Added