ENH: Dont allow length=1 regressions/correlations.

They're not meaningful, and they cause warnings from numpy.

Implemented in terms of a new preprocessor, `expect_bounded`, which
takes a tuple of `upper_bound` and `lower_bound`.
This commit is contained in:
Scott Sanderson
2016-09-02 12:49:09 -04:00
parent 959baf7fe0
commit dee715cff2
3 changed files with 122 additions and 3 deletions
+91
View File
@@ -503,6 +503,97 @@ def expect_element(*_pos, **named):
return preprocess(**valmap(_expect_element, named))
def expect_bounded(**named):
"""
Preprocessing decorator that verifies inputs fall between upper and lower
bounds.
Usage
-----
>>> @expect_bounded(x=(1, 5))
... def foo(x):
... return x + 1
...
>>> foo(1)
2
>>> foo(5)
6
>>> foo(6) # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
Traceback (most recent call last):
...
ValueError: ...foo() expected a value between 1 and 5 for argument 'x',
but got 6 instead.
Notes
-----
None can be passed as the lower or upper bound to signify that a value only
has an upper or lower bound.
>>> @expect_bounded(x=(2, None))
... def foo(x):
... return x
...
>>> foo(100000)
100000
>>> foo(1) # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
Traceback (most recent call last):
...
ValueError: ...foo() expected a value greater than or equal to 2 for
argument 'x', but got 1 instead.
>>> @expect_bounded(x=(None, 5))
... def foo(x):
... return x
...
>>> foo(6) # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
Traceback (most recent call last):
...
ValueError: ...foo() expected a value less than or equal to 5 for
argument 'x', but got 6 instead.
"""
def valid_bounds(t):
return (
isinstance(t, tuple)
and len(t) == 2
and t != (None, None)
)
for name, bounds in iteritems(named):
if not valid_bounds(bounds):
raise TypeError(
"expect_bounded() expected a tuple of bounds for"
" argument '{name}', but got {bounds} instead.".format(
name=name,
bounds=bounds,
)
)
def _expect_bounded(bounds):
(lower, upper) = bounds
if lower is None:
should_fail = lambda value: value > upper
predicate_descr = "less than or equal to " + str(upper)
elif upper is None:
should_fail = lambda value: value < lower
predicate_descr = "greater than or equal to " + str(lower)
else:
should_fail = lambda value: not (lower <= value <= upper)
predicate_descr = "between %s and %s" % bounds
template = (
"%(funcname)s() expected a value {predicate}"
" for argument '%(argname)s', but got %(actual)s instead."
).format(predicate=predicate_descr)
return make_check(
exc_type=ValueError,
template=template,
pred=should_fail,
actual=repr,
)
return preprocess(**valmap(_expect_bounded, named))
def expect_dimensions(**dimensions):
"""
Preprocessing decorator that verifies inputs are numpy arrays with a