Repository navigation
improve SOFA attribute and string variable handling and testing thereof - #149
f-brinkmann wants to merge 5 commits into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
UTF-8 byte-array comparisons can raise decoding errors, and custom netCDF string variables lack dimension metadata needed for reliable read/write round trips.
2 open issues
What changed in this PR
This PR addresses #147 by improving UTF-8 handling in SOFA file I/O, writing text as standard-compliant netCDF character data while accepting netCDF string data when reading.
Changes:
- Updates text encoding, decoding, comparison, and byte-length calculation.
- Adds tests for UTF-8 data and netCDF attribute types.
- Raises minimum requirements to Python 3.11 and netCDF4 1.7.5, removes the NumPy upper bound, and updates CI and documentation.
| File | Description |
|---|---|
| tests/test_io.py | Adds encoding and netCDF type checks. |
| sofar/utils.py | Changes string comparisons to Unicode. |
| sofar/sofastream.py | Accepts netCDF string variables during verification. |
| sofar/sofa.py | Calculates string lengths in UTF-8 bytes. |
| sofar/io.py | Updates text serialization and decoding. |
| README.md | Updates the minimum Python version. |
| pyproject.toml | Revises Python and dependency requirements. |
| HISTORY.rst | Documents text handling and support changes. |
| .circleci/config.yml | Removes unsupported Python versions from CI. |
Review effort: Balanced
| if file[var].dtype in ["S1", str]: | ||
| dtype = "string" |
| try: | ||
| assert np.all( | ||
| np.squeeze(a).astype("S") == np.squeeze(b).astype("S")) | ||
| np.squeeze(a).astype("U") == np.squeeze(b).astype("U")) |
hoyer-a
left a comment
There was a problem hiding this comment.
As far as i can judge, this looks good!
Co-authored-by: Anton Hoyer <156099087+hoyer-a@users.noreply.github.com>
|
|
||
| Deprecated | ||
| ^^^^^^^^^^ | ||
| - Python versions below 3.11 and netCDF4 versions below 1.7.5 (PR #149) |
There was a problem hiding this comment.
dropping support is not really deprecation. Maybe rephrase?
| # 'string'. To be user friendly, sofar reads netCDF data of | ||
| # type 'char' ("S1") and 'string' (str). | ||
| # (the datatype of 'string' is a VLType but its dtype is str) | ||
| if file[var].dtype in ["S1", str]: |
There was a problem hiding this comment.
| if file[var].dtype in ["S1", str]: | |
| if file[var].dtype == str: | |
| dimensions += "S" | |
| dtype = "string" | |
| elif file[var].dtype == "S1": |
I think Copilot has a point here. For string, we need to append S otherwise, we might get a shape mismatch in write_sofa(). Maybe we also should test this?
| if hasattr(sofa, key): | ||
| setattr(sofa, key, value) | ||
| else: | ||
| dimensions = "".join(list(netcdf_variable.dimensions)) |
There was a problem hiding this comment.
Same here.
| dimensions = "".join(list(netcdf_variable.dimensions)) | |
| if netcdf_variable.dtype == str: | |
| dimensions += "S" |
| # return character variables as raw bytes for decoding in | ||
| # `_format_value_from_netcdf` | ||
| file.set_auto_chartostring(False) |
There was a problem hiding this comment.
Is this really necessary? I think disabling this breaks cases where _Encoding was used.
There was a problem hiding this comment.
You can try this:
from pathlib import Path
from tempfile import TemporaryDirectory
import netCDF4
import numpy as np
import sofar as sf
with TemporaryDirectory() as tmp:
path = Path(tmp) / "encoding.sofa"
sf.write_sofa(path, sf.Sofa("SimpleFreeFieldHRIR"))
with netCDF4.Dataset(path, "a") as nc:
nc.createDimension("L", 4)
var = nc.createVariable("Legacy", "S1", ("L",))
var[:] = np.frombuffer(b"M\xf6p", dtype="S1")
var._Encoding = "latin-1"
print(sf.read_sofa(path, verify=False, verbose=False).Legacy)which worked on main but fails now with UnicodeDecodeError because _Encoding is ignored and UTF-8 is assumed.

Which issue(s) are closed by this pull request?
Closes #147 and fixes #140. Also fixes failing tests in #148
Changes proposed in this pull request:
Improved writing of SOFA attributes and string variables to be fully conform with the SOFA standard
chararray as required by the SOFA standardsofar.io._format_value_for/from_netcdfand the use of the netCDF functionsstringtocharandchartostringthat caused the error in the first place is now avoided.stringto be user friendly and backwards compatibleAll changes are fully backwards compatible because they only changed how data are handled internally in
SofaandSofaStream.AI disclaimer:
The fixes were done by an LMM. I checked the SOFA standard before hand. I discussed and instructed the LMM with care, and checked all output.