From b732b2ee67ea14a7f720bc4c018471d15dac2079 Mon Sep 17 00:00:00 2001 From: Utkarsh Kukreti Date: Wed, 15 Apr 2026 19:43:21 +0530 Subject: [PATCH] feat: implement scale_factor and add_offset getters for both readers Adds scale_factor and add_offset getters for both OmFileReader and OmFileReaderAsync. Both of these follow a similar approach as the existing compression_name getter - raising an error when called on non-array values. --- python/omfiles/_rust/__init__.pyi | 32 +++++++++++++++++++++++++++++++ src/reader.rs | 28 +++++++++++++++++++++++++++ src/reader_async.rs | 28 +++++++++++++++++++++++++++ tests/test_read_write.py | 22 ++++++++++++++++++++- 4 files changed, 109 insertions(+), 1 deletion(-) diff --git a/python/omfiles/_rust/__init__.pyi b/python/omfiles/_rust/__init__.pyi index 17e60f1..87a2869 100644 --- a/python/omfiles/_rust/__init__.pyi +++ b/python/omfiles/_rust/__init__.pyi @@ -103,6 +103,22 @@ class OmFileReader: str: Compression type of the variable. """ @property + def scale_factor(self) -> builtins.float: + r""" + Get the scale factor of the variable. + + Returns: + float: Scale factor stored in the array metadata. + """ + @property + def add_offset(self) -> builtins.float: + r""" + Get the add offset of the variable. + + Returns: + float: Add offset stored in the array metadata. + """ + @property def num_children(self) -> builtins.int: r""" Number of children of the variable. @@ -355,6 +371,22 @@ class OmFileReaderAsync: str: Compression type of the variable. """ @property + def scale_factor(self) -> builtins.float: + r""" + Get the scale factor of the variable. + + Returns: + float: Scale factor stored in the array metadata. + """ + @property + def add_offset(self) -> builtins.float: + r""" + Get the add offset of the variable. + + Returns: + float: Add offset stored in the array metadata. + """ + @property def num_children(self) -> builtins.int: r""" Number of children of the variable. diff --git a/src/reader.rs b/src/reader.rs index b2c1934..1e5be9f 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -412,6 +412,34 @@ impl OmFileReader { }) } + /// Get the scale factor of the variable. + /// + /// Returns: + /// float: Scale factor stored in the array metadata. + #[getter] + fn scale_factor(&self) -> PyResult { + self.with_reader(|reader| { + Ok(reader + .expect_array() + .map_err(|_| Self::only_arrays_error())? + .scale_factor()) + }) + } + + /// Get the add offset of the variable. + /// + /// Returns: + /// float: Add offset stored in the array metadata. + #[getter] + fn add_offset(&self) -> PyResult { + self.with_reader(|reader| { + Ok(reader + .expect_array() + .map_err(|_| Self::only_arrays_error())? + .add_offset()) + }) + } + /// Number of children of the variable. /// /// Returns: diff --git a/src/reader_async.rs b/src/reader_async.rs index 73ae766..9cbcc06 100644 --- a/src/reader_async.rs +++ b/src/reader_async.rs @@ -365,6 +365,34 @@ impl OmFileReaderAsync { }) } + /// Get the scale factor of the variable. + /// + /// Returns: + /// float: Scale factor stored in the array metadata. + #[getter] + fn scale_factor(&self) -> PyResult { + self.with_reader(|reader| { + Ok(reader + .expect_array() + .map_err(|_| Self::only_arrays_error())? + .scale_factor()) + }) + } + + /// Get the add offset of the variable. + /// + /// Returns: + /// float: Add offset stored in the array metadata. + #[getter] + fn add_offset(&self) -> PyResult { + self.with_reader(|reader| { + Ok(reader + .expect_array() + .map_err(|_| Self::only_arrays_error())? + .add_offset()) + }) + } + /// Number of children of the variable. /// /// Returns: diff --git a/tests/test_read_write.py b/tests/test_read_write.py index 41504d9..42fc231 100644 --- a/tests/test_read_write.py +++ b/tests/test_read_write.py @@ -49,6 +49,8 @@ def test_round_trip_array_datatypes(): # Read data back reader = omfiles.OmFileReader(temp_file.name) + assert reader.scale_factor == 10000.0 + assert reader.add_offset == 0.0 read_data = reader[:] reader.close() @@ -68,6 +70,8 @@ def test_write_contiguous_array_succeeds(empty_temp_om_file): writer.close(variable) reader = omfiles.OmFileReader(empty_temp_om_file) + assert reader.scale_factor == 10000.0 + assert reader.add_offset == 0.0 read_data = reader[:] reader.close() @@ -116,7 +120,7 @@ def test_write_hierarchical_file(empty_temp_om_file): # Write child1 array with attribute children child1_var = writer.write_array( - child1_data, chunks=[2, 2], name="child1", scale_factor=100000.0, children=[meta1_var, meta2_var, meta3_var] + child1_data, chunks=[2, 2], name="child1", scale_factor=10000.0, children=[meta1_var, meta2_var, meta3_var] ) # Write root array with children @@ -141,6 +145,8 @@ def test_write_hierarchical_file(empty_temp_om_file): # Verify child1 data child1_reader = reader._init_from_variable(child_metadata["/root/child1"]) + assert child1_reader.scale_factor == 10000.0 + assert child1_reader.add_offset == 0.0 read_child1 = child1_reader[:] np.testing.assert_array_almost_equal(read_child1, child1_data, decimal=4) assert read_child1.shape == (5, 5) @@ -148,6 +154,8 @@ def test_write_hierarchical_file(empty_temp_om_file): # Verify child2 data child2_reader = reader._init_from_variable(child_metadata["/root/child2"]) + assert child2_reader.scale_factor == 100000.0 + assert child2_reader.add_offset == 0.0 read_child2 = child2_reader[:] np.testing.assert_array_almost_equal(read_child2, child2_data, decimal=4) assert read_child2.shape == (3, 3) @@ -172,6 +180,12 @@ def test_write_hierarchical_file(empty_temp_om_file): assert type(metadata3) == str assert metadata_reader3.dtype == str + for metadata_reader in [metadata_reader1, metadata_reader2, metadata_reader3]: + with pytest.raises(ValueError): + metadata_reader.scale_factor + with pytest.raises(ValueError): + metadata_reader.add_offset + reader.close() child1_reader.close() child2_reader.close() @@ -184,6 +198,8 @@ def test_write_hierarchical_file(empty_temp_om_file): async def test_read_async(temp_om_file): with await omfiles.OmFileReaderAsync.from_path(temp_om_file) as reader: # Test basic async read + assert reader.scale_factor == 1.0 + assert reader.add_offset == 0.0 data = await reader.read_array((slice(0, 5), ...)) assert data.shape == (5, 5) assert data.dtype == np.float32 @@ -273,6 +289,10 @@ def test_child_traversal(temp_hierarchical_om_file): assert reader.dtype == type(None) with pytest.raises(ValueError): _ = reader.compression_name + with pytest.raises(ValueError): + _ = reader.scale_factor + with pytest.raises(ValueError): + _ = reader.add_offset with pytest.raises(ValueError): _ = reader.read_scalar() with pytest.raises(ValueError):