|
1 | 1 | from __future__ import division |
2 | 2 | import os |
| 3 | +import os.path as osp |
3 | 4 | from threading import Thread |
4 | 5 | from functools import partial |
5 | 6 | from glob import glob |
@@ -467,11 +468,28 @@ def tmp_it(): |
467 | 468 | store_mod, store_cls = ds.psy.data_store |
468 | 469 | if store_mod is not None: |
469 | 470 | store = ds._file_obj |
470 | | - # try several datasets |
471 | | - for func in get_fname_funcs: |
472 | | - fname = func(store) |
473 | | - if fname is not None: |
474 | | - break |
| 471 | + # try several engines |
| 472 | + if hasattr(store, 'file_objs'): |
| 473 | + fname = [] |
| 474 | + store_mod = [] |
| 475 | + store_cls = [] |
| 476 | + for obj in store.file_objs: # mfdataset |
| 477 | + _fname = None |
| 478 | + for func in get_fname_funcs: |
| 479 | + if _fname is None: |
| 480 | + _fname = func(obj) |
| 481 | + if _fname is not None: |
| 482 | + fname.append(_fname) |
| 483 | + store_mod.append(obj.__module__) |
| 484 | + store_cls.append(obj.__class__.__name__) |
| 485 | + fname = tuple(fname) |
| 486 | + store_mod = tuple(store_mod) |
| 487 | + store_cls = tuple(store_cls) |
| 488 | + else: |
| 489 | + for func in get_fname_funcs: |
| 490 | + fname = func(store) |
| 491 | + if fname is not None: |
| 492 | + break |
475 | 493 | # check if paths is provided and if yes, save the file |
476 | 494 | if fname is None and paths is not None: |
477 | 495 | fname = next(paths, None) |
@@ -1722,8 +1740,8 @@ def open_dataset(filename_or_obj, decode_cf=True, decode_times=True, |
1722 | 1740 | xarray.Dataset |
1723 | 1741 | The dataset that contains the variables from `filename_or_obj`""" |
1724 | 1742 | # use the absolute path name (is saver when saving the project) |
1725 | | - if isstring(filename_or_obj) and os.path.exists(filename_or_obj): |
1726 | | - filename_or_obj = os.path.abspath(filename_or_obj) |
| 1743 | + if isstring(filename_or_obj) and osp.exists(filename_or_obj): |
| 1744 | + filename_or_obj = osp.abspath(filename_or_obj) |
1727 | 1745 | if engine == 'gdal': |
1728 | 1746 | from psyplot.gdal_store import GdalStore |
1729 | 1747 | filename_or_obj = GdalStore(filename_or_obj) |
@@ -1795,9 +1813,10 @@ def open_mfdataset(paths, decode_cf=True, decode_times=True, |
1795 | 1813 | paths, decode_cf=decode_cf, decode_times=decode_times, engine=engine, |
1796 | 1814 | decode_coords=False, **kwargs) |
1797 | 1815 | if decode_cf: |
1798 | | - return CFDecoder.decode_ds(ds, gridfile=gridfile, inplace=True, |
1799 | | - decode_coords=decode_coords, |
1800 | | - decode_times=decode_times) |
| 1816 | + ds = CFDecoder.decode_ds(ds, gridfile=gridfile, inplace=True, |
| 1817 | + decode_coords=decode_coords, |
| 1818 | + decode_times=decode_times) |
| 1819 | + ds.psy._concat_dim = kwargs.get('concat_dim') |
1801 | 1820 | return ds |
1802 | 1821 |
|
1803 | 1822 |
|
@@ -2387,7 +2406,7 @@ def filter_attrs(item): |
2387 | 2406 | queues[0].task_done() |
2388 | 2407 | self._new_dims = {} |
2389 | 2408 | self.onupdate.emit() |
2390 | | - except: |
| 2409 | + except Exception: |
2391 | 2410 | self._finish_all(queues) |
2392 | 2411 | raise |
2393 | 2412 | return InteractiveBase.start_update(self, draw=draw, queues=queues) |
@@ -2873,15 +2892,20 @@ def sel_method(key, dims, name=None): |
2873 | 2892 | return instance |
2874 | 2893 |
|
2875 | 2894 | @classmethod |
2876 | | - def _get_dsnames(cls, data, ignore_keys=['attrs', 'plotter', 'ds']): |
| 2895 | + def _get_dsnames(cls, data, ignore_keys=['attrs', 'plotter', 'ds'], |
| 2896 | + concat_dim=False): |
2877 | 2897 | """Recursive method to get all the file names out of a dictionary |
2878 | 2898 | `data` created with the :meth`array_info` method""" |
2879 | 2899 | def filter_ignores(item): |
2880 | 2900 | return item[0] not in ignore_keys and isinstance(item[1], dict) |
2881 | 2901 | if 'fname' in data: |
2882 | | - return {(data['fname'], data['store'])} |
2883 | | - return set(chain(*map(cls._get_dsnames, dict( |
2884 | | - filter(filter_ignores, six.iteritems(data))).values()))) |
| 2902 | + return {tuple( |
| 2903 | + [data['fname'], data['store']] + |
| 2904 | + ([data.get('concat_dim')] if concat_dim else []))} |
| 2905 | + return set(chain(*map(partial(cls._get_dsnames, concat_dim=concat_dim, |
| 2906 | + ignore_keys=ignore_keys), |
| 2907 | + dict(filter(filter_ignores, |
| 2908 | + six.iteritems(data))).values()))) |
2885 | 2909 |
|
2886 | 2910 | @classmethod |
2887 | 2911 | def _get_ds_descriptions( |
@@ -3022,29 +3046,38 @@ def only_filter(arr_name, info): |
3022 | 3046 | return arr_name in save_only |
3023 | 3047 | save_only = only |
3024 | 3048 | only = None |
| 3049 | + |
| 3050 | + def get_fname_use(fname): |
| 3051 | + squeeze = isstring(fname) |
| 3052 | + fname = safe_list(fname) |
| 3053 | + ret = tuple(f if utils.is_remote_url(f) or osp.isabs(f) else |
| 3054 | + osp.join(pwd, f) |
| 3055 | + for f in fname) |
| 3056 | + return ret[0] if squeeze else ret |
| 3057 | + |
3025 | 3058 | if not isinstance(alternative_paths, dict): |
3026 | 3059 | it = iter(alternative_paths) |
3027 | 3060 | alternative_paths = defaultdict(partial(next, it, None)) |
3028 | 3061 | # first open all datasets if not already done |
3029 | 3062 | if datasets is None: |
3030 | | - names_and_stores = cls._get_dsnames(d) |
| 3063 | + replace_concat_dim = 'concat_dim' not in kwargs |
| 3064 | + |
| 3065 | + names_and_stores = cls._get_dsnames(d, concat_dim=True) |
3031 | 3066 | datasets = {} |
3032 | | - for fname, (store_mod, store_cls) in names_and_stores: |
| 3067 | + for fname, (store_mod, store_cls), concat_dim in names_and_stores: |
3033 | 3068 | fname_use = fname |
3034 | 3069 | got = True |
| 3070 | + if replace_concat_dim and concat_dim is not None: |
| 3071 | + kwargs['concat_dim'] = concat_dim |
| 3072 | + elif replace_concat_dim and concat_dim is None: |
| 3073 | + kwargs.pop('concat_dim', None) |
3035 | 3074 | try: |
3036 | 3075 | fname_use = alternative_paths[fname] |
3037 | 3076 | except KeyError: |
3038 | 3077 | got = False |
3039 | 3078 | if not got or not fname_use: |
3040 | 3079 | if fname is not None: |
3041 | | - if utils.is_remote_url(fname): |
3042 | | - fname_use = fname |
3043 | | - else: |
3044 | | - if os.path.isabs(fname): |
3045 | | - fname_use = fname |
3046 | | - else: |
3047 | | - fname_use = os.path.join(pwd, fname) |
| 3080 | + fname_use = get_fname_use(fname) |
3048 | 3081 | if fname_use is not None: |
3049 | 3082 | datasets[fname] = _open_ds_from_store( |
3050 | 3083 | fname_use, store_mod, store_cls, **kwargs) |
@@ -3176,7 +3209,7 @@ def array_info(self, dump=None, paths=None, attrs=True, |
3176 | 3209 | saved_ds = kwargs.pop('_saved_ds', {}) |
3177 | 3210 |
|
3178 | 3211 | def get_alternative(f): |
3179 | | - return next(filter(lambda t: os.path.samefile(f, t[0]), |
| 3212 | + return next(filter(lambda t: osp.samefile(f, t[0]), |
3180 | 3213 | six.iteritems(alternative_paths)), [False, f]) |
3181 | 3214 |
|
3182 | 3215 | if copy: |
@@ -3240,15 +3273,17 @@ def copy_obj(obj): |
3240 | 3273 | else: |
3241 | 3274 | found, f = get_alternative(f) |
3242 | 3275 | if use_rel_paths: |
3243 | | - f = os.path.relpath(f, pwd) |
| 3276 | + f = osp.relpath(f, pwd) |
3244 | 3277 | else: |
3245 | | - f = os.path.abspath(f) |
| 3278 | + f = osp.abspath(f) |
3246 | 3279 | d['fname'].append(f) |
3247 | 3280 | if fname is None or isinstance(fname, |
3248 | 3281 | six.string_types): |
3249 | 3282 | d['fname'] = d['fname'][0] |
3250 | 3283 | else: |
3251 | 3284 | d['fname'] = tuple(safe_list(fname)) |
| 3285 | + if arr.psy.base.psy._concat_dim is not None: |
| 3286 | + d['concat_dim'] = arr.psy.base.psy._concat_dim |
3252 | 3287 | if 'ds' in ds_description: |
3253 | 3288 | if full_ds: |
3254 | 3289 | d['ds'] = copy_obj(arr.psy.base) |
@@ -3680,6 +3715,9 @@ class DatasetAccessor(object): |
3680 | 3715 | _num = None |
3681 | 3716 | _plot = None |
3682 | 3717 |
|
| 3718 | + #: The concatenation dimension for datasets opened with open_mfdataset |
| 3719 | + _concat_dim = None |
| 3720 | + |
3683 | 3721 | @property |
3684 | 3722 | def num(self): |
3685 | 3723 | """A unique number for the dataset""" |
@@ -3721,7 +3759,7 @@ def filename(self): |
3721 | 3759 | """The name of the file that stores this dataset""" |
3722 | 3760 | fname = self._filename |
3723 | 3761 | if fname is None: |
3724 | | - fname, store_mod, store_cls = get_filename_ds(self.ds, dump=False) |
| 3762 | + fname = get_filename_ds(self.ds, dump=False)[0] |
3725 | 3763 | return fname |
3726 | 3764 |
|
3727 | 3765 | @filename.setter |
@@ -3889,7 +3927,7 @@ def start_update(self, draw=None, queues=None): |
3889 | 3927 | for arr in self: |
3890 | 3928 | arr.psy.start_update(draw=False) |
3891 | 3929 | self.onupdate.emit() |
3892 | | - except: |
| 3930 | + except Exception: |
3893 | 3931 | self._finish_all(queues) |
3894 | 3932 | raise |
3895 | 3933 | if queues is not None: |
@@ -3987,6 +4025,22 @@ def _open_ds_from_store(fname, store_mod=None, store_cls=None, **kwargs): |
3987 | 4025 | """Open a dataset and return it""" |
3988 | 4026 | if isinstance(fname, xr.Dataset): |
3989 | 4027 | return fname |
| 4028 | + if not isstring(fname): |
| 4029 | + try: # test iterable |
| 4030 | + fname[0] |
| 4031 | + except TypeError: |
| 4032 | + pass |
| 4033 | + else: |
| 4034 | + if store_mod is not None and store_cls is not None: |
| 4035 | + if isstring(store_mod): |
| 4036 | + store_mod = repeat(store_mod) |
| 4037 | + if isstring(store_cls): |
| 4038 | + store_cls = repeat(store_cls) |
| 4039 | + fname = [getattr(import_module(sm), sc)(f) |
| 4040 | + for sm, sc, f in zip(store_mod, store_cls, fname)] |
| 4041 | + kwargs['engine'] = None |
| 4042 | + kwargs['lock'] = False |
| 4043 | + return open_mfdataset(fname, **kwargs) |
3990 | 4044 | if store_mod is not None and store_cls is not None: |
3991 | 4045 | fname = getattr(import_module(store_mod), store_cls)(fname) |
3992 | 4046 | return open_dataset(fname, **kwargs) |
|
0 commit comments