From 0075e314a38d2052f0082a4d673c68576ca16008 Mon Sep 17 00:00:00 2001 From: "Philipp S. Sommer" Date: Thu, 9 Apr 2020 10:50:27 +0200 Subject: [PATCH 1/4] bug fix and allow different decoder arguments This commit fixes a bug when speciying x, y, z and t as input arguments to a CFDecoder instance. Furthermore, we the decoder parameter for the from_dataset method now supports different input types, a None, a CFDecoder instance, a CFDecoder class and keyword arguments as a python dict --- psyplot/data.py | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/psyplot/data.py b/psyplot/data.py index ac04b90..f266eae 100755 --- a/psyplot/data.py +++ b/psyplot/data.py @@ -550,9 +550,9 @@ def logger(self, value): def __init__(self, ds=None, x=None, y=None, z=None, t=None): self.ds = ds self.x = rcParams['decoder.x'].copy() if x is None else set(x) - self.y = rcParams['decoder.y'].copy() if x is None else set(y) - self.z = rcParams['decoder.z'].copy() if x is None else set(z) - self.t = rcParams['decoder.t'].copy() if x is None else set(t) + self.y = rcParams['decoder.y'].copy() if y is None else set(y) + self.z = rcParams['decoder.z'].copy() if z is None else set(z) + self.t = rcParams['decoder.t'].copy() if t is None else set(t) @staticmethod def register_decoder(decoder_class, pos=0): @@ -599,7 +599,7 @@ def can_decode(cls, ds, var): @classmethod @docstrings.dedent - def get_decoder(cls, ds, var): + def get_decoder(cls, ds, var, *args, **kwargs): """ Class method to get the right decoder class that can decode the given dataset and variable @@ -615,8 +615,8 @@ def get_decoder(cls, ds, var): `var`""" for decoder_cls in cls._registry: if decoder_cls.can_decode(ds, var): - return decoder_cls(ds) - return CFDecoder(ds) + return decoder_cls(ds, *args, **kwargs) + return CFDecoder(ds, *args, **kwargs) @staticmethod @docstrings.get_sectionsf('CFDecoder.decode_coords', sections=[ @@ -3568,8 +3568,14 @@ def from_dataset(cls, base, method='isel', default_slice=None, Index (e.g. 0 if `method` is 'isel') that shall be used for dimensions not covered by `dims` and `furtherdims`. If None, the whole slice will be used. - decoder: CFDecoder - The decoder that shall be used to decoder the `base` dataset + decoder: CFDecoder or dict + Arguments for the decoder. This can be one of + + - an instance of :class:`CFDecoder` + - a subclass of :class:`CFDecoder` + - a dictionary with keyword-arguments to the automatically + determined decoder class + - None to automatically set the decoder squeeze: bool, optional Default True. If True, and the created arrays have a an axes with length 1, it is removed from the dimension list (e.g. an array @@ -3634,12 +3640,18 @@ def ds2arr(arr): arr.attrs.update(attrs) return arr - if decoder is not None: - def get_decoder(arr): - return decoder - else: - def get_decoder(arr): + decoder_input = decoder + + def get_decoder(arr): + print(decoder_input) + if decoder_input is None: return CFDecoder.get_decoder(base, arr) + elif isinstance(decoder_input, CFDecoder): + return decoder_input + elif isinstance(decoder_input, dict): + return CFDecoder.get_decoder(base, arr, **decoder_input) + else: + return decoder_input(base) def add_missing_dimensions(arr): # add the missing dimensions to the dataset. This is not anymore @@ -3678,7 +3690,8 @@ def sel_method(key, dims, name=None): ret = squeeze_array(arr.isel(**dims)) # delete the variable dimension for the idims dims.pop('variable', None) - ret.psy.init_accessor(arr_name=key, base=base, idims=dims) + ret.psy.init_accessor(arr_name=key, base=base, idims=dims, + decoder=decoder) return maybe_load(ret) else: def sel_method(key, dims, name=None): @@ -3706,7 +3719,7 @@ def sel_method(key, dims, name=None): ret = squeeze_array(arr.sel(**dims)) else: ret = squeeze_array(arr.sel(method=method, **dims)) - ret.psy.init_accessor(arr_name=key, base=base) + ret.psy.init_accessor(arr_name=key, base=base, decoder=decoder) return maybe_load(ret) if 'name' not in kwargs: default_names = list( From 6e7049a4af3fd6e0c6450fb65ea27256708f1f3e Mon Sep 17 00:00:00 2001 From: "Philipp S. Sommer" Date: Thu, 9 Apr 2020 10:51:03 +0200 Subject: [PATCH 2/4] add decoder args to changelog --- CHANGELOG.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3bed241..fb3b227 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,11 @@ v1.2.2 ====== Added ----- +* ``ArrayList.from_dataset`` (and consecutively all plotmethods) now support + different input types for the decoder. You can pass an instance of the + ``CFDecoder`` class, a sub class of ``CFDecoder``, or keyword arguments + that are used to initialize the decoder, + see `#20 `__ * `psyplot.data.open_dataset` now decodes grid_mappings attributes, see `#17 `__ * psyplot projects now support the with syntax, e.g. something like:: @@ -20,6 +25,8 @@ Changed are ignored * If a given variable cannot be found in the provided coords to ``CFDecoder.get_variable_by_axis``, we fall back to the ``CFDecoder.ds.coords`` attribute, see `#19 `__ +* A bug has been fixed for initializing a ``CFDecoder`` with ``x, y, z`` and + ``t`` parameters (see `#20 `__) v1.2.1 From e6999d01334101c0fe3355ac4fa75165e5726e72 Mon Sep 17 00:00:00 2001 From: "Philipp S. Sommer" Date: Thu, 9 Apr 2020 10:51:16 +0200 Subject: [PATCH 3/4] add tests for the different decoder args --- tests/test_data.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/test_data.py b/tests/test_data.py index 166381d..50cf9f9 100755 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -1144,6 +1144,34 @@ def test_from_dataset_12_list_and_2_vars(self): self.assertEqual(len(l[0]), 2) self.assertEqual(len(l[1]), 2) + def test_from_dataset_13_decoder_class(self): + ds = xr.Dataset(*self._from_dataset_test_variables) + + class MyDecoder(psyd.CFDecoder): + pass + + l = self.list_class.from_dataset(ds, name="v2", decoder=MyDecoder) + self.assertIsInstance(l[0].psy.decoder, MyDecoder) + + def test_from_dataset_14_decoder_instance(self): + ds = xr.Dataset(*self._from_dataset_test_variables) + + class MyDecoder(psyd.CFDecoder): + pass + + decoder = MyDecoder(ds) + + l = self.list_class.from_dataset(ds, name="v2", decoder=decoder) + self.assertIs(l[0].psy.decoder, decoder) + + def test_from_dataset_15_decoder_kws(self): + ds = xr.Dataset(*self._from_dataset_test_variables) + + l = self.list_class.from_dataset(ds, name="v2", + decoder=dict(x={'myx'})) + self.assertEqual(l[0].psy.decoder.x, {'myx'}) + + def test_array_info(self): variables, coords = self._from_dataset_test_variables variables['v4'] = variables['v3'].copy() From f814da1a5727b82d77636024de4b98ac93997080 Mon Sep 17 00:00:00 2001 From: "Philipp S. Sommer" Date: Thu, 9 Apr 2020 13:16:29 +0200 Subject: [PATCH 4/4] removed debug statement --- psyplot/data.py | 1 - 1 file changed, 1 deletion(-) diff --git a/psyplot/data.py b/psyplot/data.py index f266eae..44603ae 100755 --- a/psyplot/data.py +++ b/psyplot/data.py @@ -3643,7 +3643,6 @@ def ds2arr(arr): decoder_input = decoder def get_decoder(arr): - print(decoder_input) if decoder_input is None: return CFDecoder.get_decoder(base, arr) elif isinstance(decoder_input, CFDecoder):