Source code for obspy.core.util.misc

# -*- coding: utf-8 -*-
"""
Various additional utilities for ObsPy.

:copyright:
    The ObsPy Development Team (devs@obspy.org)
:license:
    GNU Lesser General Public License, Version 3
    (http://www.gnu.org/copyleft/lesser.html)
"""
from contextlib import contextmanager
import os
import sys
import inspect
from subprocess import Popen, PIPE
import warnings
import itertools
import tempfile
import numpy as np


# The following dictionary maps the first character of the channel_id to the
# lowest sampling rate this so called Band Code should be used for according
# to: SEED MANUAL p.124
# We use this e.g. in seihub.client.getWaveform to request two samples more on
# both start and end to cut to the samples that really are nearest to requested
# start/endtime afterwards.
BAND_CODE = {'F': 1000.0,
             'G': 1000.0,
             'D': 250.0,
             'C': 250.0,
             'E': 80.0,
             'S': 10.0,
             'H': 80.0,
             'B': 10.0,
             'M': 1.0,
             'L': 1.0,
             'V': 0.1,
             'U': 0.01,
             'R': 0.0001,
             'P': 0.000001,
             'T': 0.0000001,
             'Q': 0.00000001}


[docs]def guessDelta(channel): """ Estimate time delta in seconds between each sample from given channel name. :type channel: str :param channel: Channel name, e.g. ``'BHZ'`` or ``'H'`` :rtype: float :return: Returns ``0`` if band code is not given or unknown. .. rubric:: Example >>> print(guessDelta('BHZ')) 0.1 >>> print(guessDelta('H')) 0.0125 >>> print(guessDelta('XZY')) # doctest: +SKIP 0 """ try: return 1. / BAND_CODE[channel[0]] except: msg = "No or unknown channel id provided. Specifying a channel id " + \ "could lead to better selection of first/last samples of " + \ "fetched traces." warnings.warn(msg) return 0
[docs]def scoreatpercentile(values, per, limit=(), issorted=True): """ Calculates the score at the given per percentile of the sequence a. For example, the score at ``per=50`` is the median. If the desired quantile lies between two data points, we interpolate between them. If the parameter ``limit`` is provided, it should be a tuple (lower, upper) of two values. Values of ``a`` outside this (closed) interval will be ignored. .. rubric:: Examples >>> a = [1, 2, 3, 4] >>> scoreatpercentile(a, 25) 1.75 >>> scoreatpercentile(a, 50) 2.5 >>> scoreatpercentile(a, 75) 3.25 >>> a = [6, 47, 49, 15, 42, 41, 7, 255, 39, 43, 40, 36, 500] >>> scoreatpercentile(a, 25, limit=(0, 100)) 25.5 >>> scoreatpercentile(a, 50, limit=(0, 100)) 40 >>> scoreatpercentile(a, 75, limit=(0, 100)) 42.5 This function is taken from :func:`scipy.stats.scoreatpercentile`. Copyright (c) Gary Strangman """ if limit: values = [v for v in values if limit[0] < v < limit[1]] if issorted: values = sorted(values) def _interpolate(a, b, fraction): return a + (b - a) * fraction idx = per / 100. * (len(values) - 1) if (idx % 1 == 0): return values[int(idx)] else: return _interpolate(values[int(idx)], values[int(idx) + 1], idx % 1)
[docs]def flatnotmaskedContiguous(a): """ Find contiguous unmasked data in a masked array along the given axis. This function is taken from :func:`numpy.ma.extras.flatnotmasked_contiguous`. Copyright (c) Pierre Gerard-Marchant """ np.ma.extras.flatnotmasked_contiguous m = np.ma.getmask(a) if m is np.ma.nomask: return slice(0, a.size, None) i = 0 result = [] for (k, g) in itertools.groupby(m.ravel()): n = len(list(g)) if not k: result.append(slice(i, i + n)) i += n return result or None
[docs]def complexifyString(line): """ Converts a string in the form "(real, imag)" into a complex type. :type line: str :param line: String in the form ``"(real, imag)"``. :rtype: complex :return: Complex number. .. rubric:: Example >>> complexifyString("(1,2)") (1+2j) >>> complexifyString(" ( 1 , 2 ) ") (1+2j) """ temp = line.split(',') return complex(float(temp[0].strip()[1:]), float(temp[1].strip()[:-1]))
[docs]def toIntOrZero(value): """ Converts given value to an integer or returns 0 if it fails. :param value: Arbitrary data type. :rtype: int numpy.version.version .. rubric:: Example >>> toIntOrZero("12") 12 >>> toIntOrZero("x") 0 """ try: return int(value) except ValueError: return 0 # import numpy loadtxt and check if ndlim parameter is available
try: from numpy import loadtxt loadtxt(np.array([]), ndlim=1) except TypeError: # otherwise redefine loadtxt
[docs] def loadtxt(*args, **kwargs): """ Replacement for older numpy.loadtxt versions not supporting ndlim parameter. """ if not 'ndlim' in kwargs: return np.loadtxt(*args, **kwargs) # ok we got a ndlim param if kwargs['ndlim'] != 1: # for now we support only one dimensional arrays raise NotImplementedError('Upgrade your NumPy version!') del kwargs['ndlim'] dtype = kwargs.get('dtype', None) # lets get the data try: data = np.loadtxt(*args, **kwargs) except IOError, e: # raises in older versions if no data could be read if 'reached before encountering data' in str(e): # return empty array return np.array([], dtype=dtype) # otherwise just raise raise # ensures that an array is returned return np.atleast_1d(data)
[docs]def get_untracked_files_from_git(): """ Tries to return a list of files (absolute paths) that are untracked by git in the repository. Returns `None` if the system call to git fails. """ dir_ = os.path.abspath( os.path.dirname(inspect.getfile(inspect.currentframe()))) dir_ = os.path.dirname(os.path.dirname(os.path.dirname(dir_))) try: # Check that the git root directory is actually the ObsPy directory. p = Popen(['git', 'rev-parse', '--show-toplevel'], cwd=dir_, stdout=PIPE, stderr=PIPE) p.stderr.close() git_root_dir = p.stdout.readlines()[0].strip() if git_root_dir != dir_: raise Exception p = Popen(['git', 'status', '-u', '--porcelain'], cwd=dir_, stdout=PIPE, stderr=PIPE) p.stderr.close() stdout = p.stdout.readlines() files = [os.path.abspath(os.path.join(dir_, line.split()[1].strip())) for line in stdout if line.startswith("??")] except: return None return files
[docs]def wrap_long_string(string, line_length=79, prefix="", special_first_prefix=None, assumed_tab_width=8, sloppy=False): """ Reformat a long string, wrapping it to a specified length. :type string: str :param string: Input string to wrap :type line_length: int :param line_length: total target length of each line, including the prefix if specified :type prefix: str, optional :param prefix: common prefix used to start the line (e.g. some spaces, tabs for indentation) :type special_first_prefix: str, optional :param special_first_prefix: special prefix to use on the first line, instead of the general prefix :type assumed_tab_width: int :param assumed_tab_width: if the prefix strings include tabs the line length can not be computed exactly. assume a tab in general is equivalent to this many spaces. :type sloppy: bool :param sloppy: Controls the behavior when a single word without spaces is to long to fit on a single line. Default (False) is to allow a single line to be longer than the specified line length. If set to True, Long words will be force-hyphenated to fit the line. .. rubric:: Examples >>> string = ("Retrieve an event based on the unique origin " ... "ID numbers assigned by the IRIS DMC") >>> print wrap_long_string(string, prefix="\t*\t > ", ... line_length=50) # doctest: +SKIP * > Retrieve an event based on * > the unique origin ID numbers * > assigned by the IRIS DMC >>> print wrap_long_string(string, prefix="\t* ", ... line_length=70) # doctest: +SKIP * Retrieve an event based on the unique origin ID * numbers assigned by the IRIS DMC >>> print wrap_long_string(string, prefix="\t \t > ", ... special_first_prefix="\t*\t", ... line_length=50) # doctest: +SKIP * Retrieve an event based on > the unique origin ID numbers > assigned by the IRIS DMC >>> problem_string = ("Retrieve_an_event_based_on_the_unique " ... "origin ID numbers assigned by the IRIS DMC") >>> print wrap_long_string(problem_string, prefix="\t\t", ... line_length=40, sloppy=True) # doctest: +SKIP Retrieve_an_event_based_on_the_unique origin ID numbers assigned by the IRIS DMC >>> print wrap_long_string(problem_string, prefix="\t\t", ... line_length=40) # doctest: +SKIP Retrieve_an_event_base\ d_on_the_unique origin ID numbers assigned by the IRIS DMC """ def text_width_for_prefix(line_length, prefix): text_width = line_length - len(prefix) - \ (assumed_tab_width - 1) * prefix.count("\t") return text_width lines = [] if special_first_prefix is not None: text_width = text_width_for_prefix(line_length, special_first_prefix) else: text_width = text_width_for_prefix(line_length, prefix) while len(string) > text_width: ind = string.rfind(" ", 0, text_width) # no suitable place to split found if ind < 1: # sloppy: search to right for space to split at if sloppy: ind = string.find(" ", text_width) if ind == -1: ind = len(string) - 1 part = string[:ind] string = string[ind + 1:] # not sloppy: force hyphenate else: ind = text_width - 2 part = string[:ind] + "\\" string = string[ind:] # found a suitable place to split else: part = string[:ind] string = string[ind + 1:] # need to use special first line prefix? if special_first_prefix is not None and not lines: line = special_first_prefix + part else: line = prefix + part lines.append(line) # need to set default text width, just in case we had a different # text width for the first line text_width = text_width_for_prefix(line_length, prefix) lines.append(prefix + string) return "\n".join(lines)
@contextmanager
[docs]def CatchOutput(): """ A context manager that catches stdout/stderr for its scope. Always use with "with" statement. Does nothing otherwise. Roughly based on: http://stackoverflow.com/a/17954769 This variant does not leak file descriptors. >>> with CatchOutput() as out: # doctest: +SKIP ... os.system('echo "mystdout"') ... os.system('echo "mystderr" >&2') >>> print out.stdout # doctest: +SKIP mystdout >>> print out.stderr # doctest: +SKIP mystderr """ stdout_file, stdout_filename = tempfile.mkstemp(prefix="obspy-") stderr_file, stderr_filename = tempfile.mkstemp(prefix="obspy-") try: fd_stdout = sys.stdout.fileno() fd_stderr = sys.stderr.fileno() # Dummy class to transport the output. class Output(): pass out = Output() out.stdout = "" out.stderr = "" with os.fdopen(os.dup(sys.stdout.fileno()), "w") as old_stdout: with os.fdopen(os.dup(sys.stderr.fileno()), "w") as old_stderr: sys.stdout.flush() sys.stderr.flush() os.dup2(stdout_file, fd_stdout) os.dup2(stderr_file, fd_stderr) os.close(stdout_file) os.close(stderr_file) try: yield out finally: sys.stdout.flush() sys.stderr.flush() os.fsync(sys.stdout.fileno()) os.fsync(sys.stderr.fileno()) sys.stdout = sys.__stdout__ sys.stderr = sys.__stderr__ sys.stdout.flush() sys.stderr.flush() os.dup2(old_stdout.fileno(), sys.stdout.fileno()) os.dup2(old_stderr.fileno(), sys.stderr.fileno()) with open(stdout_filename, "r") as fh: out.stdout = fh.read() with open(stderr_filename, "r") as fh: out.stderr = fh.read() finally: # Make sure to always close and remove the temporary files. try: os.close(stdout_file) except: pass try: os.close(stderr_file) except: pass try: os.remove(stdout_filename) except OSError: pass try: os.remove(stderr_filename) except OSError: pass
if __name__ == '__main__': import doctest doctest.testmod(exclude_empty=True)