Skip to content

parse_value returns string values with their <...> delimiters attached (#176) - #179

Merged
headmeister merged 1 commit into
isi-nmr:masterfrom
gdevenyi:fix/jcampdx-string-delimiters
Jul 27, 2026
Merged

parse_value returns string values with their <...> delimiters attached (#176)#179
headmeister merged 1 commit into
isi-nmr:masterfrom
gdevenyi:fix/jcampdx-string-delimiters

Conversation

@gdevenyi

Copy link
Copy Markdown
Contributor

Fixes #176.

Spec 2.2: the angle brackets delimit a JCAMP-DX string, they are not part of its value. parse_value returned whole <...> matches, so every consumer had to strip them:

>>> from brukerapi.jcampdx import GenericParameter as G
>>> G.parse_value("<abc>"), G.parse_value("<>"), G.parse_value("<a> <b>")
('abc', '', array(['a', 'b'], dtype='<U1'))

<> reads as the empty string, per point 4 of the issue. A caller still cannot tell ##$KEY=<> from ##$KEY= by value — both are ''.

What changed

  • _STRING_RE captures the inside, keeping the two-alternative escape handling from Parse escaped delimiters and nested structs instead of discarding them (J3, J4, J5) #158, and every parse path unquotes. A value whose elements are all delimited strings skips the numeric casts, because <1> <2> is a list of the strings "1" and "2" and never of two numbers.
  • Writing stays symmetric. Nothing about a Python str says whether it is a JCAMP string or an undelimited enum symbol, so a parameter keeps whichever form it was already written in. That is what makes read-modify-write — FrameGroupSplitter, SlicePackageSplitter — put <FG_SLICE> and <a.u.> back as they were, while VisuCoreFrameType=MAGNITUDE_IMAGE stays bare.
  • The workarounds go: the 26 [1:-1] slices and the delimited literals in brukerapi/config/, and the .strip("<>") / f"<{fg}>" guards in dataset.py, schemas.py, splitters.py and mergers.py.
  • Three recipes used [1:-1] to stop #PARAM.method(...) from being read as a #PARAM + accessor substitution; they take parentheses instead ((#ACQ_sw_version).replace(...)).
  • Property references, dim_type expectations, docs and the split example follow.

Effect on the public API

dim_type and metadata are the visible ones:

>>> dataset.dim_type
['spatial', 'spatial', 'FG_SLICE', 'FG_DIFFUSION']      # was ['...', '<FG_SLICE>', '<FG_DIFFUSION>']
>>> dataset.metadata['visu_subject']['name']
'CIC_LRMousePhantom'                                     # was '<CIC_LRMousePhantom>'; subj_id already said this

FrameGroupSplitter("FG_ECHO") and filter(parameter='PULPROG', value='RARE.ppg') take the undelimited name; the delimited form no longer matches.

Verification

  • Corpus of 3,468 targets: 0 differences. Same load outcome, shape, dtype and error message for every single one, before and after.
  • Property references regenerate with the delimiters dropped and nothing else changed — every one of the 171 changed lines is a <X>X; no affine, date, shape or id moved.
  • Write fidelity unchanged: all 143 real visu_pars still rewrite byte-for-byte. (99 reco files differ in where the 78-column wrap falls, identically on master — pre-existing, not from this change.)
  • Suite: 2114 passed, 12 skipped. The 5 test_affine_agrees_with_the_paravision_nifti_sform failures are the same ones master has.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NuK1cZi8U54WXAdXMmGpzy

…Fixes isi-nmr#176

Spec 2.2: the angle brackets delimit a JCAMP-DX string, they are not part of
its value. `parse_value` returned whole `<...>` matches, so every consumer had
to strip them -- which the library itself did in 26 places across the property
recipes, and which `metadata` never did, leaving two brukerapi APIs reporting
different values for the same parameter.

`_STRING_RE` now captures the inside, and every parse path unquotes: a value of
all-delimited strings skips the numeric casts, since `<1> <2>` is a list of two
strings and never of two numbers. `<>` reads as the empty string.

Nothing about a Python string says whether it is a JCAMP string or an
undelimited enum symbol, so writing has to be told. A parameter keeps whichever
form it was already written in, which is what makes read-modify-write -- the
frame-group and slice-package splitters -- put `<FG_SLICE>` and `<a.u.>` back
as they were, while `VisuCoreFrameType=MAGNITUDE_IMAGE` stays bare.

With that in place the workarounds go: the 26 `[1:-1]` slices and the delimited
literals in brukerapi/config, and the `.strip("<>")` and `f"<{fg}>"` guards in
dataset, schemas, splitters and mergers. Three recipes that relied on `[1:-1]`
to break the `#PARAM.accessor` substitution needed parentheses instead.

Verified against the 3,468-target corpus: identical load outcome, shape, dtype
and error message for every one. The property references regenerate with the
delimiters dropped and nothing else changed, and every visu_pars still rewrites
byte-for-byte.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NuK1cZi8U54WXAdXMmGpzy
Copilot AI review requested due to automatic review settings July 26, 2026 18:27

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@headmeister
headmeister merged commit 05e019d into isi-nmr:master Jul 27, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

parse_value returns string values with their <...> delimiters attached

3 participants