Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Lib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

# Errors
from .error import CDMSError # noqa
from .error import CdunifError # noqa
from .error import ReadOnlyKeyError # noqa
from lazy_object_proxy import Proxy
from . import dataset
from . import selectors
Expand Down Expand Up @@ -137,5 +139,3 @@
from . import dask_protocol # noqa
except BaseException:
pass

from .Cdunif import CdunifError
8 changes: 7 additions & 1 deletion Lib/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -2090,7 +2090,13 @@ def createVariableCopy(self, var, id=None, attributes=None, axes=None, extbounds
if attname not in ["id", "datatype", "parent"]:
if isinstance(attval, string_types):
attval = str(attval)
setattr(newvar, str(attname), attval)
try:
setattr(newvar, str(attname), attval)
except Cdunif.ReadOnlyKeyError as e:
# Suppress the exception context
err = "Cannot write read-only attribute '{!s}', must be " \
"removed from '{!s}' variable before writing to file".format(e, var.id)
raise CDMSError(err) from None
if (attname == "_FillValue") or (attname == "missing_value"):
setattr(newvar, "_FillValue", attval)
setattr(newvar, "missing_value", attval)
Expand Down
2 changes: 2 additions & 0 deletions Lib/error.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"Error object for cdms module"

from .Cdunif import CDMSError # noqa: F401
from .Cdunif import CdunifError # noqa: F401
from .Cdunif import ReadOnlyKeyError # noqa: F401
7 changes: 1 addition & 6 deletions Lib/fvariable.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,7 @@ def __setattr__(self, name, value):
if hasattr(self, "parent") and self.parent is None:
raise CDMSError(FileClosedWrite + self.id)
if (name not in self.__cdms_internals__) and (value is not None):
try:
setattr(self._obj_, str(name), value)
except Exception:
raise CDMSError(
"Setting %s.%s=%s" %
(self.id, name, repr(value)))
setattr(self._obj_, str(name), value)
self.attributes[name] = value
self.__dict__[name] = value

Expand Down
22 changes: 21 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ build: install-conda prep-feedstock create-conda-env
EXTRA_CB_OPTIONS='--croot $(LOCAL_CHANNEL_DIR)' \
$(SCRIPTS_DIR)/build_steps.sh | tee $(WORK_DIR)/`cat $(PWD)/.variant`

.PHONY: build-env
build-env:
$(CONDA_ENV); \
$(CONDA_ACTIVATE) base; \
$(CONDA_RC); \
conda activate build; \
bash

.PHONY: build-docs
build-docs: ENV := docs
build-docs: CHANNELS := -c file://$(LOCAL_CHANNEL_DIR) -c conda-forge
Expand Down Expand Up @@ -126,14 +134,26 @@ test: create-conda-env
$(CONDA_RC); \
conda config --set channel_priority strict; \
conda activate $(ENV); \
conda install --yes $(CHANNELS) cdms2 testsrunner cdat_info pytest pip \
conda install --yes $(CHANNELS) cdms2 testsrunner cdat_info pytest pip nco \
$(CONDA_TEST_PACKAGES); \
conda info; \
conda list --explicit > $(TEST_OUTPUT_DIR)/environment.txt; \
python run_tests.py -H -v2 -n 1

cp -rf $(PWD)/tests_html $(TEST_OUTPUT_DIR)/

.PHONY: test-env
test-env:
$(CONDA_ENV); \
$(CONDA_ACTIVATE) base; \
$(CONDA_RC); \
conda activate test; \
bash

.PHONY: test-clean
test-clean:
rm -rf $(ENVS_DIR)/test

.PHONY: upload
upload: ENV := upload
upload: PACKAGES := 'python=3.8' anaconda-client
Expand Down
12 changes: 8 additions & 4 deletions Src/Cdunifmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ PyThread_type_lock Cdunif_lock;
#endif

static PyObject *CdunifError;
static PyObject *ReadOnlyKeyError;
static PyObject *CDMSError;

/* Set error string */
Expand Down Expand Up @@ -2098,10 +2099,10 @@ static int PyCdunifFile_SetAttribute(PyCdunifFileObject *self, PyObject *nameobj
char *name = PyStr_AsString(nameobj);
if (check_if_open(self, 1)) {
if (strcmp(name, "dimensions") == 0
|| strcmp(name, "variables") == 0
|| strcmp(name, "variables") == 0
|| strcmp(name, "dimensioninfo") == 0
|| strcmp(name, "__dict__") == 0) {
PyErr_SetString(PyExc_TypeError, "object has read-only attributes");
PyErr_Format(ReadOnlyKeyError, "%s", name);
return -1;
}
define_mode(self, 1);
Expand Down Expand Up @@ -2454,9 +2455,10 @@ static int PyCdunifVariable_SetAttribute(PyCdunifVariableObject *self,
PyObject *nameobj, PyObject *value) {
char *name = PyStr_AsString(nameobj);
if (check_if_open(self->file, 1)) {
if (strcmp(name, "shape") == 0 || strcmp(name, "dimensions") == 0
if (strcmp(name, "shape") == 0
|| strcmp(name, "dimensions") == 0
|| strcmp(name, "__dict__") == 0) {
PyErr_SetString(PyExc_TypeError, "object has read-only attributes");
PyErr_Format(ReadOnlyKeyError, "%s", name);
return -1;
}
define_mode(self->file, 1);
Expand Down Expand Up @@ -3489,9 +3491,11 @@ MODULE_INIT_FUNC (Cdunif) {

CDMSError = PyErr_NewException("cdms2.CDMSError", NULL, NULL);
CdunifError = PyErr_NewException("Cdunif.CdunifError", CDMSError, NULL);
ReadOnlyKeyError = PyErr_NewException("Cdunif.ReadOnlyKeyError", CDMSError, NULL);

PyDict_SetItemString(d, "CDMSError", CDMSError);
PyDict_SetItemString(d, "CdunifError", CdunifError);
PyDict_SetItemString(d, "ReadOnlyKeyError", ReadOnlyKeyError);

/* Check for errors */
if (PyErr_Occurred())
Expand Down
31 changes: 31 additions & 0 deletions tests/test_dataset_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import string
import os
import sys
import cdat_info
import subprocess
import shutil

cdms2.setNetcdfUseParallelFlag(0)

Expand All @@ -21,6 +24,34 @@ def setUp(self):
def testFileAttributes(self):
self.assertEqual(self.file.id, "test")

def testWriteReadOnlyAttribute(self):
out = self.getTempFile("read_only.nc", "w")
out.write(self.u)
out.close()

temp = self.getTempFile("read_only.nc", "a")

with self.assertRaises(cdms2.ReadOnlyKeyError):
setattr(temp, "dimensions", "test")

with self.assertRaises(cdms2.ReadOnlyKeyError):
setattr(temp["u"], "dimensions", "test")

def testWriteThroughReadOnlyAtttribute(self):
iname = os.path.join(cdat_info.get_sampledata_path(), "clt.nc")
oname = os.path.join(os.getcwd(), "clt-modded.nc")

shutil.copyfile(iname, oname)

subprocess.run(["ncatted", "-a", "dimensions,clt,c,c,'lat lon'", oname])

ifile = cdms2.open(oname)
data = ifile("clt")
ifile.close()

with cdms2.open(oname, "w") as ofile, self.assertRaises(cdms2.CDMSError):
ofile.write(data)

def testScalarSlice(self):
u = self.u
scalar = u[0,0,0]
Expand Down