# -*- coding: utf-8 -*-
import numpy as np
[docs]class _Validator:
"""Performs internal validations on various input types."""
[docs] def integer(self, item, desc):
"""Validates an integer.
Parameters
----------
item: int
The item to validate.
desc: str
A description of the item being validated.
Returns
-------
item: int
The original input item if valid.
Raises
------
TypeError
If the value is not an ``int``.
Examples
--------
>>> validator = _Validator()
>>> n_crops = 3
>>> validator.integer(n_crops, 'n_crops (number of sections to crop)')
"""
if not isinstance(item, int):
raise TypeError("Expected {} to be an integer".format(desc))
return item
[docs] def real(self, item, desc):
"""Validates a real number.
Parameters
----------
item: float
The item to validate.
desc: str
A description of the item being validated.
Returns
-------
item: float
The original input item if valid.
Raises
------
TypeError
If the value cannot be converted into a ``float``.
Examples
--------
>>> validator = _Validator()
>>> var = 5.0
>>> validator.real(var, 'var (variance)')
"""
try:
return float(item)
except ValueError:
raise TypeError("Expected {} to be a real number".format(desc))
[docs] def string(self, item, desc):
"""Validates a string.
Parameters
----------
item: str
The item to validate.
desc: str
A description of the item being validated.
Returns
-------
item: str
The original input item if valid.
Raises
------
TypeError
If the value is not a ``str``.
Examples
--------
>>> validator = _Validator()
>>> method = 'median'
>>> method = validator.string(method, 'method (filter type)')
"""
if not isinstance(item, str):
raise TypeError("Expected {} to be a string".format(desc))
return item
[docs] def boolean(self, item, desc):
"""Validates a boolean.
Parameters
----------
item: bool
The item to validate.
desc: str
A description of the item being validated.
Returns
-------
item: bool
The original input item if valid.
Raises
------
TypeError
If the value is not ``True`` or ``False``.
Examples
--------
>>> validator = _Validator()
>>> indep = True
>>> indep = validator.boolean(indep, 'indep (whether to independently normalize channels)')
"""
if not isinstance(item, bool):
raise TypeError("Expected {} to be a boolean".format(desc))
return item
[docs] def one_of(self, item, desc, items):
"""Validates that an item is one of some permitted values.
Parameters
----------
item: Any
The item to validate.
desc: str
A description of the item being validated.
items: Iterable[Any]
The collection of permitted values to check against.
Returns
-------
item: Any
The original input item if valid.
Raises
------
ValueError
If the value is not one of the specified permitted values.
Examples
--------
>>> validator = _Validator()
>>> method = 'median'
>>> method = validator.one_of(method, 'method (filter type)', ['median', 'mean'])
"""
if not item in items:
raise ValueError('Expected {} to be one of {}'.format(desc, items))
return item
[docs] def restricted_integer(self, item, desc, condition, expected):
"""Validates an integer and checks that it satisfies some condition.
Parameters
----------
item: int
The item to validate.
desc: str
A description of the item being validated.
condition: lambda
A condition to check the item against.
expected: str
A description of the condition, or expected value.
Returns
-------
item: int
The original input item if valid.
Raises
------
ValueError
If the ``int`` value does not satisfy the provided condition.
Examples
--------
>>> validator = _Validator()
>>> n_crops = 3
>>> n_crops = validator.restricted_integer(
>>> n_crops, 'n_crops (number of sections to crop)',
>>> lambda x: 0 <= x <= 5, 'between zero and five')
"""
if isinstance(item, int):
if not condition(item):
raise ValueError('Expected {} to be {}'.format(desc, expected))
else:
raise TypeError("Expected {} to be an integer".format(desc))
return item
[docs] def restricted_float(self, item, desc, condition, expected):
"""Validates a float and checks that it satisfies some condition.
Parameters
----------
item: float
The item to validate.
desc: str
A description of the item being validated.
condition: lambda
A condition to check the item against.
expected: str
A description of the condition, or expected value.
Returns
-------
item: float
The original input item if valid.
Raises
------
ValueError
If the ``float`` value does not satisfy the provided condition.
Examples
--------
>>> validator = _Validator()
>>> var = 5.0
>>> validator.restricted_float(var, 'var (variance)', lambda x: x > 0, 'positive')
"""
item = self.real(item, desc)
if not condition(item):
raise ValueError('Expected {} to be {}'.format(desc, expected))
else:
return item
[docs] def integer_value(self, item, desc, condition, expected):
"""Validates an integer value or value range and checks that it satisfies some condition.
Parameters
----------
item: int
The item to validate.
desc: str
A description of the item being validated.
condition: lambda
A condition to check the item against.
expected: str
A description of the condition, or expected value.
Returns
-------
item: tuple(int, int)
The value range if the input item is valid.
Raises
------
TypeError
If the value is not a ``int`` or ``(int, int)``.
ValueError
If the value is a tuple with more than two elements (sanity check).
Examples
--------
>>> validator = _Validator()
>>> validator.integer_value(5, 'window_size (window size)', lambda a, b: 0 < a <= b, 'positive')
>>> #=> (5, 5)
>>> validator.integer_value((5, 10), 'window_size (window size)', lambda a, b: 0 < a <= b, 'positive')
>>> #=> (5, 10)
"""
if isinstance(item, tuple):
if len(item) == 2:
self.restricted_integer(item[0], 'lower limit for {}'.format(desc),
lambda a: condition(a, a), expected)
self.restricted_integer(item[1], 'upper limit for {}'.format(desc),
lambda b: condition(item[0], b), '{} and greater than or equal to the lower limit'.format(expected))
return item
else:
raise ValueError('Expected range for {} to be a tuple (lower, upper) representing the limits ' \
'of the range of values from which the value is drawn'.format(desc, desc))
elif isinstance(item, int):
item = self.restricted_integer(item, desc, lambda x: condition(x, x), expected)
return (item, item)
else:
raise TypeError('Expected range for {} to be a tuple (lower, upper) representing the limits ' \
'of the range of values from which the value is drawn'.format(desc, desc))
[docs] def float_value(self, item, desc, condition, expected):
"""Validates a float value or value range and checks that it satisfies some condition.
Parameters
----------
item: float
The item to validate.
desc: str
A description of the item being validated.
condition: lambda
A condition to check the item against.
expected: str
A description of the condition, or expected value.
Returns
-------
item: tuple(float, float)
The value range if the input item is valid.
Raises
------
TypeError
If the value is not a ``float`` or ``(float, float)``.
ValueError
If the value is a tuple with more than two elements (sanity check).
Examples
--------
>>> validator = _Validator()
>>> validator.float_value(5.0, 'var (variance)', lambda a, b: 0 < a <= b, 'positive')
>>> #=> (5.0, 5.0)
>>> validator.float_value((5, 10), 'var (variance)', lambda a, b: 0 < a <= b, 'positive')
>>> #=> (5.0, 10.0)
"""
if isinstance(item, tuple):
if len(item) == 2:
self.restricted_float(item[0], 'lower limit for {}'.format(desc),
lambda a: condition(a, a), expected)
self.restricted_float(item[1], 'upper limit for {}'.format(desc),
lambda b: condition(item[0], b), '{} and greater than or equal to the lower limit'.format(expected))
return item
else:
raise ValueError('Expected range for {} to be a tuple (lower, upper) representing the limits ' \
'for a uniform distribution from which the value is sampled'.format(desc, desc))
elif isinstance(item, (int, float)):
item = self.restricted_float(item, desc, lambda x: condition(x, x), expected)
return (item, item)
else:
raise TypeError('Expected range for {} to be a tuple (lower, upper) representing the limits ' \
'for a uniform distribution from which the value is sampled'.format(desc, desc))
[docs] def signal(self, signal):
"""Validates a WAV audio signal.
Parameters
----------
signal: numpy.ndarray [shape (T,) or (1xT) for mono, (2xT) for stereo]
The input signal to validate.
Returns
-------
signal: numpy.ndarray [shape (T,) for mono, (2xT) for stereo]
The original signal if it is valid.
Raises
------
TypeError
- If the signal is of the wrong type.
- If the signal is not a floating point ``numpy.ndarray``.
- If the signal is a floating point ``numpy.ndarray`` with samples outside :math:`[-1, 1]`.
ValueError
- If the signal is a ``numpy.ndarray`` with more than two dimensions.
- If the signal has more than two channels.
"""
if isinstance(signal, np.ndarray):
if any(signal.dtype == np.dtype(type_) for type_ in (np.float16, np.float32, np.float64)):
if (signal.min() >= -1) and (signal.max() <= 1):
if signal.ndim == 1:
return signal.reshape(1, -1).astype(np.float32)
elif signal.ndim == 2:
if any(n_channels in signal.shape for n_channels in (1, 2)):
a, b = signal.shape
return (signal.T if b in (1, 2) else signal).astype(np.float32)
else:
raise ValueError('Expected signal to be mono (T,) or stereo (2xT)')
else:
raise ValueError('Expected signal to be a 1D or 2D numpy.ndarray')
else:
raise TypeError('Expected signal to have samples in range [-1, 1]')
else:
raise TypeError('Expected a floating point signal')
else:
raise TypeError('Expected signal to be a numpy.ndarray')
[docs] def random_state(self, state):
"""Validates a random state object or seed.
Parameters
----------
state: None, int, numpy.random.RandomState
A random state object or seed.
Returns
-------
state: numpy.random.RandomState
A random state object.
Raises
------
TypeError
If the random state object is of the incorrect type.
"""
if isinstance(state, int) or (state is None):
return np.random.RandomState(seed=state)
elif isinstance(state, np.random.RandomState):
return state
else:
raise TypeError('Expected random state to be of type: None, int, or numpy.random.RandomState')