Skip to content

Commit

Permalink
Merge pull request #1384 from RandallPittmanOrSt/disable_dataset_iter…
Browse files Browse the repository at this point in the history
…ation

Disable Dataset iteration and membership operations (raise error on __iter__ and __contains__)
  • Loading branch information
jswhit authored Nov 12, 2024
2 parents f7b00f8 + 38f5aaa commit 4e994e7
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 1 deletion.
3 changes: 3 additions & 0 deletions src/netCDF4/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,9 @@ class Dataset:
def has_bzip2_filter(self) -> bool: ...
def has_szip_filter(self) -> bool: ...
def __getitem__(self, elem: str) -> Any: ... # should be Group | Variable, but this causes too many problems
# __iter__ and __contains__ always error because iteration and membership ops are not allowed
def __iter__(self) -> NoReturn: ...
def __contains__(self, key) -> NoReturn: ...
def __setattr__(self, name: str, value: Any) -> None: ...
def __getattr__(self, name: str) -> Any: ...
def __delattr__(self, name: str): ...
Expand Down
13 changes: 12 additions & 1 deletion src/netCDF4/_netCDF4.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -2569,6 +2569,17 @@ strings.
else:
raise IndexError('%s not found in %s' % (lastname,group.path))

def __iter__(self):
raise TypeError(
"Dataset is not iterable. Consider iterating on Dataset.variables."
)

def __contains__(self, key):
raise TypeError(
"Dataset does not support membership operations. Perhaps try 'varname in"
" dataset.variables' or 'dimname in dataset.dimensions'."
)

def filepath(self,encoding=None):
"""**`filepath(self,encoding=None)`**
Expand Down Expand Up @@ -4041,7 +4052,7 @@ behavior is similar to Fortran or Matlab, but different than numpy.
If fill_value is set to `False`, then the variable is not pre-filled.
The default netCDF fill values can be found in the dictionary `netCDF4.default_fillvals`.
If not set, the default fill value will be used but no `_FillValue` attribute will be created
(this is the default behavior of the netcdf-c library). If you want to use the
(this is the default behavior of the netcdf-c library). If you want to use the
default fill value, but have the `_FillValue` attribute set, use
`fill_value='default'` (note - this only works for primitive data types). `Variable.get_fill_value`
can be used to retrieve the fill value, even if the `_FillValue` attribute is not set.
Expand Down
34 changes: 34 additions & 0 deletions test/test_no_iter_contains.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import os
import tempfile
import unittest

import netCDF4

FILE_NAME = tempfile.NamedTemporaryFile(suffix='.nc', delete=False).name


class TestNoIterNoContains(unittest.TestCase):
def setUp(self) -> None:
self.file = FILE_NAME
with netCDF4.Dataset(self.file, "w") as dataset:
# just create a simple variable
dataset.createVariable("var1", int)

def tearDown(self) -> None:
os.remove(self.file)

def test_no_iter(self) -> None:
"""Verify that iteration is explicitly not supported"""
with netCDF4.Dataset(self.file, "r") as dataset:
with self.assertRaises(TypeError):
for _ in dataset: # type: ignore # type checker catches that this doesn't work
pass

def test_no_contains(self) -> None:
"""Verify the membership operations are explicity not supported"""
with netCDF4.Dataset(self.file, "r") as dataset:
with self.assertRaises(TypeError):
_ = "var1" in dataset

if __name__ == "__main__":
unittest.main(verbosity=2)

0 comments on commit 4e994e7

Please sign in to comment.