Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix ci builds (PPSD and others) #2424

Merged
merged 10 commits into from Jul 19, 2019
7 changes: 7 additions & 0 deletions .travis.yml
Expand Up @@ -79,6 +79,8 @@ install:
- hash -r
- conda config --set always_yes yes --set changeps1 no
- conda update -q conda
- conda config --set restore_free_channel true
- conda config --add channels conda-forge
# Useful for debugging any issues with conda
- conda info -a
- |
Expand All @@ -89,6 +91,7 @@ install:
# no basemap version works with this old numpy, so leave out
BASEMAP=""
PYPROJ="pyproj"
GFORTRAN="libgfortran=1"
PROJ4="proj4"
# ancient matplotlib needs to be turned to AGG before anything else,
# otherwise it tries to import incompatible version of Qt as a backend
Expand All @@ -104,6 +107,7 @@ install:
BASEMAP="basemap"
PYPROJ="pyproj"
PROJ4="proj4"
GFORTRAN=""
# elif [[ "$ARCHITECTURE_32BIT" == "True" ]]; then
# # Special packages for 32bit. Also let conda resolve to the latest
# # versions.
Expand All @@ -122,13 +126,15 @@ install:
BASEMAP="basemap"
PYPROJ="pyproj"
PROJ4="proj4=4.9.3"
GFORTRAN=""
else
NUMPY="numpy"
SCIPY="scipy"
MATPLOTLIB="matplotlib>=2.0.0"
BASEMAP="basemap"
PYPROJ="pyproj"
PROJ4="proj4"
GFORTRAN=""
fi
- |
if [[ "${PYTHON_VERSION}" == '3.7' ]]; then
Expand All @@ -145,6 +151,7 @@ install:
$BASEMAP
$PYPROJ
$PROJ4
$GFORTRAN
coverage
decorator
docopt
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.txt
Expand Up @@ -125,6 +125,7 @@ master:
- obspy.signal.polarization:
* Fix an issue in selecting Z/N/E traces from given stream (see #2365)
- obspy.signal.PPSD:
* Changed numpy-based serialization as to not require pickling (see #2424).
* Fixed exact trace cutting for PSD segments (see #2040).
* Timestamp representations internally and in npz I/O were changed to use
integer nanosecond POSIX timestamps to avoid any potential floating point
Expand Down
8 changes: 5 additions & 3 deletions obspy/imaging/source.py
Expand Up @@ -62,11 +62,13 @@ def _setup_figure_and_axes(kind, fig=None, subplot_size=4.0):
ncols_ = len(row)
for j, kind__ in enumerate(row):
kind_.append(kind__)
kwargs = {"aspect": "equal", "adjustable": "datalim"}
kwargs = {"adjustable": "datalim"}
if kind__ in ("p_quiver", "p_sphere", "s_quiver", "s_sphere"):
kwargs["projection"] = "3d"
axes.append(fig.add_subplot(
nrows, ncols_, i * ncols_ + j + 1, **kwargs))
else: # equal aspect never worked on 3d plot, see mpl #13474
kwargs["aspect"] = "equal"
ax = fig.add_subplot(nrows, ncols_, i * ncols_ + j + 1, **kwargs)
axes.append(ax)
return fig, axes, kind_


Expand Down
20 changes: 17 additions & 3 deletions obspy/signal/spectral_estimation.py
Expand Up @@ -222,6 +222,10 @@ class PPSD(object):
NPZ_STORE_KEYS_LIST_TYPES +
NPZ_STORE_KEYS_SIMPLE_TYPES +
NPZ_STORE_KEYS_VERSION_NUMBERS)
# A mapping of values for storing info in the NPZ file. This is needed
# because some types are not loadable without allowing pickle (#2409).
NPZ_SIMPLE_TYPE_MAP = {None: ''}
NPZ_SIMPLE_TYPE_MAP_R = {v: i for i, v in NPZ_SIMPLE_TYPE_MAP.items()}

def __init__(self, stats, metadata, skip_on_gaps=False,
db_bins=(-200, -50, 1.), ppsd_length=3600.0, overlap=0.5,
Expand Down Expand Up @@ -1274,7 +1278,14 @@ def save_npz(self, filename):
:type filename: str
:param filename: Name of numpy .npz output file
"""
out = dict([(key, getattr(self, key)) for key in self.NPZ_STORE_KEYS])
out = {}
for key in self.NPZ_STORE_KEYS:
value = getattr(self, key)
# Some values need to be replaced to allow non-pickle
# serialization (#2409).
if key in self.NPZ_STORE_KEYS_SIMPLE_TYPES:
value = self.NPZ_SIMPLE_TYPE_MAP.get(value, value)
out[key] = value
np.savez_compressed(filename, **out)

@staticmethod
Expand Down Expand Up @@ -1315,6 +1326,7 @@ def _load(data):
elif key in (ppsd.NPZ_STORE_KEYS_SIMPLE_TYPES +
ppsd.NPZ_STORE_KEYS_VERSION_NUMBERS):
data_ = data_.item()
data_ = ppsd.NPZ_SIMPLE_TYPE_MAP_R.get(data_, data_)
# convert floating point POSIX second timestamps from older npz
# files
if (key in ppsd.NPZ_STORE_KEYS_LIST_TYPES and
Expand All @@ -1331,7 +1343,7 @@ def _load(data):

# XXX get rid of if/else again when bumping minimal numpy to 1.7
if NUMPY_VERSION >= [1, 7]:
with np.load(filename) as data:
with np.load(filename, allow_pickle=True) as data:
return _load(data)
else:
data = np.load(filename)
Expand Down Expand Up @@ -1369,7 +1381,9 @@ def _add(data):
_check_npz_ppsd_version(self, data)
# check if all metadata agree
for key in self.NPZ_STORE_KEYS_SIMPLE_TYPES:
if getattr(self, key) != data[key].item():
value_ = data[key].item()
value = self.NPZ_SIMPLE_TYPE_MAP_R.get(value_, value_)
if getattr(self, key) != value:
msg = ("Mismatch in '%s' attribute.\n\tCurrent:\n\t%s\n\t"
"Loaded:\n\t%s")
msg = msg % (key, getattr(self, key), data[key].item())
Expand Down
20 changes: 18 additions & 2 deletions obspy/signal/tests/test_spectral_estimation.py
Expand Up @@ -9,6 +9,7 @@
from future.utils import native_str

import gzip
import io
import os
import unittest
import warnings
Expand Down Expand Up @@ -136,7 +137,7 @@ def test_obspy_psd_vs_pitsa(self):
fn_psd_pitsa = "pitsa_noise_psd_samprate_100_nfft_512_noverlap_0.npy"
file_psd_pitsa = os.path.join(self.path, fn_psd_pitsa)

noise = np.load(file_noise)
noise = np.load(file_noise, allow_pickle=True)
# in principle to mimic PITSA's results detrend should be specified as
# some linear detrending (e.g. from matplotlib.mlab.detrend_linear)
psd_obspy, _ = psd(noise, NFFT=nfft, Fs=sampling_rate,
Expand Down Expand Up @@ -802,7 +803,7 @@ def test_exception_reading_newer_npz(self):
"consider updating your ObsPy installation.".format(
PPSD(stats=Stats(), metadata=None).ppsd_version))
# 1 - loading a npz
data = np.load(self.example_ppsd_npz)
data = np.load(self.example_ppsd_npz, allow_pickle=True)
# we have to load, modify 'ppsd_version' and save the npz file for the
# test..
items = {key: data[key] for key in data.files}
Expand Down Expand Up @@ -836,6 +837,21 @@ def test_nice_ringlaser_metadata_error_msg(self):
"stating the overall sensitivity`.")
self.assertEqual(str(e.exception), expected)

def test_can_read_npz_without_pickle(self):
"""
Ensures that a default PPSD can be written and read without having to
allow np.load the use of pickle. See #2409.
"""
# Init a test PPSD and empty byte stream.
ppsd = PPSD.load_npz(self.example_ppsd_npz)
byte_me = io.BytesIO()
# Save PPSD to byte stream and rewind to 0.
ppsd.save_npz(byte_me)
byte_me.seek(0)
# Load dict, will raise an exception if pickle is needed.
loaded_dict = dict(np.load(byte_me, allow_pickle=False))
self.assertIsInstance(loaded_dict, dict)


def suite():
return unittest.makeSuite(PsdTestCase, 'test')
Expand Down