17
17
18
18
from .core import duck_array_ops , indexing , ops , utils
19
19
from .core .formatting import format_timestamp , first_n_items , last_item
20
- from .core .variable import as_variable , Variable
20
+ from .core .variable import as_variable , IndexVariable , Variable
21
21
from .core .pycompat import iteritems , OrderedDict , PY3 , basestring
22
22
23
23
32
32
'D' : 1e9 * 60 * 60 * 24 }
33
33
34
34
35
+ class SerializationWarning (RuntimeWarning ):
36
+ """Warnings about encoding/decoding issues in serialization."""
37
+
38
+
35
39
def mask_and_scale (array , fill_value = None , scale_factor = None , add_offset = None ,
36
40
dtype = float ):
37
41
"""Scale and mask array values according to CF conventions for packed and
@@ -113,15 +117,15 @@ def _decode_datetime_with_netcdf4(num_dates, units, calendar):
113
117
warnings .warn ('Unable to decode time axis into full '
114
118
'numpy.datetime64 objects, continuing using dummy '
115
119
'netCDF4.datetime objects instead, reason: dates out'
116
- ' of range' , RuntimeWarning , stacklevel = 3 )
120
+ ' of range' , SerializationWarning , stacklevel = 3 )
117
121
else :
118
122
try :
119
123
dates = nctime_to_nptime (dates )
120
124
except ValueError as e :
121
125
warnings .warn ('Unable to decode time axis into full '
122
126
'numpy.datetime64 objects, continuing using '
123
127
'dummy netCDF4.datetime objects instead, reason:'
124
- '{0}' .format (e ), RuntimeWarning , stacklevel = 3 )
128
+ '{0}' .format (e ), SerializationWarning , stacklevel = 3 )
125
129
return dates
126
130
127
131
@@ -773,7 +777,7 @@ def maybe_encode_nonstring_dtype(var, name=None):
773
777
warnings .warn ('saving variable %s with floating '
774
778
'point data as an integer dtype without '
775
779
'any _FillValue to use for NaNs' % name ,
776
- RuntimeWarning , stacklevel = 3 )
780
+ SerializationWarning , stacklevel = 3 )
777
781
data = duck_array_ops .around (data )[...]
778
782
if encoding .get ('_Unsigned' , False ):
779
783
signed_dtype = np .dtype ('i%s' % dtype .itemsize )
@@ -828,6 +832,15 @@ def _infer_dtype(array, name=None):
828
832
def ensure_dtype_not_object (var , name = None ):
829
833
# TODO: move this from conventions to backends? (it's not CF related)
830
834
if var .dtype .kind == 'O' :
835
+ if (isinstance (var , IndexVariable ) and
836
+ isinstance (var .to_index (), pd .MultiIndex )):
837
+ raise NotImplementedError (
838
+ 'variable {!r} is a MultiIndex, which cannot yet be '
839
+ 'serialized to netCDF files '
840
+ '(https://github.com/pydata/xarray/issues/1077). Use '
841
+ 'reset_index() to convert MultiIndex levels into coordinate '
842
+ 'variables instead.' .format (name ))
843
+
831
844
dims , data , attrs , encoding = _var_as_tuple (var )
832
845
missing = pd .isnull (data )
833
846
if missing .any ():
@@ -951,7 +964,7 @@ def decode_cf_variable(name, var, concat_characters=True, mask_and_scale=True,
951
964
else :
952
965
warnings .warn ("variable %r has _Unsigned attribute but is not "
953
966
"of integer type. Ignoring attribute." % name ,
954
- RuntimeWarning , stacklevel = 3 )
967
+ SerializationWarning , stacklevel = 3 )
955
968
956
969
if mask_and_scale :
957
970
if 'missing_value' in attributes :
@@ -975,7 +988,7 @@ def decode_cf_variable(name, var, concat_characters=True, mask_and_scale=True,
975
988
warnings .warn ("variable {!r} has multiple fill values {}, "
976
989
"decoding all values to NaN."
977
990
.format (name , fill_value ),
978
- RuntimeWarning , stacklevel = 3 )
991
+ SerializationWarning , stacklevel = 3 )
979
992
980
993
scale_factor = pop_to (attributes , encoding , 'scale_factor' )
981
994
add_offset = pop_to (attributes , encoding , 'add_offset' )
@@ -1185,6 +1198,16 @@ def cf_decoder(variables, attributes,
1185
1198
def _encode_coordinates (variables , attributes , non_dim_coord_names ):
1186
1199
# calculate global and variable specific coordinates
1187
1200
non_dim_coord_names = set (non_dim_coord_names )
1201
+
1202
+ for name in list (non_dim_coord_names ):
1203
+ if isinstance (name , basestring ) and ' ' in name :
1204
+ warnings .warn (
1205
+ 'coordinate {!r} has a space in its name, which means it '
1206
+ 'cannot be marked as a coordinate on disk and will be '
1207
+ 'saved as a data variable instead' .format (name ),
1208
+ SerializationWarning , stacklevel = 6 )
1209
+ non_dim_coord_names .discard (name )
1210
+
1188
1211
global_coordinates = non_dim_coord_names .copy ()
1189
1212
variable_coordinates = defaultdict (set )
1190
1213
for coord_name in non_dim_coord_names :
0 commit comments