123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342 |
- """ Test that functions are reasonably behaved with None as input.
- Typed Cython objects (like dict) may also be None. Using functions from
- Python's C API that expect a specific type but receive None instead can cause
- problems such as throwing an uncatchable SystemError (and some systems may
- segfault instead). We obviously don't what that to happen! As the tests
- below discovered, this turned out to be a rare occurence. The only changes
- required were to use `d.copy()` instead of `PyDict_Copy(d)`, and to always
- return Python objects from functions instead of int or bint (so exceptions
- can propagate).
- The vast majority of functions throw TypeError. The vast majority of
- functions also behave the same in `toolz` and `cytoolz`. However, there
- are a few minor exceptions. Since passing None to functions are edge cases
- that don't have well-established behavior yet (other than raising TypeError),
- the tests in this file serve to verify that the behavior is at least
- reasonably well-behaved and don't cause SystemErrors.
- """
- # XXX: This file could be back-ported to `toolz` once unified testing exists.
- import cytoolz
- from cytoolz import *
- from cytoolz.utils import raises
- from operator import add
- class GenException(object):
- def __init__(self, exc):
- self.exc = exc
- def __iter__(self):
- return self
- def __next__(self):
- raise self.exc
- def next(self):
- raise self.exc
- def test_dicttoolz():
- tested = []
- assert raises((TypeError, AttributeError), lambda: assoc(None, 1, 2))
- tested.append('assoc')
- assert raises((TypeError, AttributeError), lambda: dissoc(None, 1))
- tested.append('dissoc')
- # XXX
- assert (raises(TypeError, lambda: get_in(None, {})) or
- get_in(None, {}) is None)
- assert raises(TypeError, lambda: get_in(None, {}, no_default=True))
- assert get_in([0, 1], None) is None
- assert raises(TypeError, lambda: get_in([0, 1], None, no_default=True))
- tested.append('get_in')
- assert raises(TypeError, lambda: keyfilter(None, {1: 2}))
- assert raises((AttributeError, TypeError), lambda: keyfilter(identity, None))
- tested.append('keyfilter')
- # XXX
- assert (raises(TypeError, lambda: keymap(None, {1: 2})) or
- keymap(None, {1: 2}) == {(1,): 2})
- assert raises((AttributeError, TypeError), lambda: keymap(identity, None))
- tested.append('keymap')
- assert raises(TypeError, lambda: merge(None))
- assert raises((TypeError, AttributeError), lambda: merge(None, None))
- tested.append('merge')
- assert raises(TypeError, lambda: merge_with(None, {1: 2}, {3: 4}))
- assert raises(TypeError, lambda: merge_with(identity, None))
- assert raises((TypeError, AttributeError),
- lambda: merge_with(identity, None, None))
- tested.append('merge_with')
- assert raises(TypeError, lambda: update_in({1: {2: 3}}, [1, 2], None))
- assert raises(TypeError, lambda: update_in({1: {2: 3}}, None, identity))
- assert raises((TypeError, AttributeError),
- lambda: update_in(None, [1, 2], identity))
- tested.append('update_in')
- assert raises(TypeError, lambda: valfilter(None, {1: 2}))
- assert raises((AttributeError, TypeError), lambda: valfilter(identity, None))
- tested.append('valfilter')
- # XXX
- assert (raises(TypeError, lambda: valmap(None, {1: 2})) or
- valmap(None, {1: 2}) == {1: (2,)})
- assert raises((AttributeError, TypeError), lambda: valmap(identity, None))
- tested.append('valmap')
- assert (raises(TypeError, lambda: itemmap(None, {1: 2})) or
- itemmap(None, {1: 2}) == {1: (2,)})
- assert raises((AttributeError, TypeError), lambda: itemmap(identity, None))
- tested.append('itemmap')
- assert raises(TypeError, lambda: itemfilter(None, {1: 2}))
- assert raises((AttributeError, TypeError), lambda: itemfilter(identity, None))
- tested.append('itemfilter')
- assert raises((AttributeError, TypeError), lambda: assoc_in(None, [2, 2], 3))
- assert raises(TypeError, lambda: assoc_in({}, None, 3))
- tested.append('assoc_in')
- s1 = set(tested)
- s2 = set(cytoolz.dicttoolz.__all__)
- assert s1 == s2, '%s not tested for being None-safe' % ', '.join(s2 - s1)
- def test_functoolz():
- tested = []
- assert raises(TypeError, lambda: complement(None)())
- tested.append('complement')
- assert compose(None) is None
- assert raises(TypeError, lambda: compose(None, None)())
- tested.append('compose')
- assert compose_left(None) is None
- assert raises(TypeError, lambda: compose_left(None, None)())
- tested.append('compose_left')
- assert raises(TypeError, lambda: curry(None))
- tested.append('curry')
- assert raises(TypeError, lambda: do(None, 1))
- tested.append('do')
- assert identity(None) is None
- tested.append('identity')
- assert raises(TypeError, lambda: juxt(None))
- assert raises(TypeError, lambda: list(juxt(None, None)()))
- tested.append('juxt')
- assert memoize(identity, key=None)(1) == 1
- assert memoize(identity, cache=None)(1) == 1
- tested.append('memoize')
- assert raises(TypeError, lambda: pipe(1, None))
- tested.append('pipe')
- assert thread_first(1, None) is None
- tested.append('thread_first')
- assert thread_last(1, None) is None
- tested.append('thread_last')
- assert flip(lambda a, b: (a, b))(None)(None) == (None, None)
- tested.append('flip')
- assert apply(identity, None) is None
- assert raises(TypeError, lambda: apply(None))
- tested.append('apply')
- excepts(None, lambda x: x)
- excepts(TypeError, None)
- tested.append('excepts')
- s1 = set(tested)
- s2 = set(cytoolz.functoolz.__all__)
- assert s1 == s2, '%s not tested for being None-safe' % ', '.join(s2 - s1)
- def test_itertoolz():
- tested = []
- assert raises(TypeError, lambda: list(accumulate(None, [1, 2])))
- assert raises(TypeError, lambda: list(accumulate(identity, None)))
- tested.append('accumulate')
- assert raises(TypeError, lambda: concat(None))
- assert raises(TypeError, lambda: list(concat([None])))
- tested.append('concat')
- assert raises(TypeError, lambda: list(concatv(None)))
- tested.append('concatv')
- assert raises(TypeError, lambda: list(cons(1, None)))
- tested.append('cons')
- assert raises(TypeError, lambda: count(None))
- tested.append('count')
- # XXX
- assert (raises(TypeError, lambda: list(drop(None, [1, 2]))) or
- list(drop(None, [1, 2])) == [1, 2])
- assert raises(TypeError, lambda: list(drop(1, None)))
- tested.append('drop')
- assert raises(TypeError, lambda: first(None))
- tested.append('first')
- assert raises(TypeError, lambda: frequencies(None))
- tested.append('frequencies')
- assert raises(TypeError, lambda: get(1, None))
- assert raises(TypeError, lambda: get([1, 2], None))
- tested.append('get')
- assert raises(TypeError, lambda: groupby(None, [1, 2]))
- assert raises(TypeError, lambda: groupby(identity, None))
- tested.append('groupby')
- assert raises(TypeError, lambda: list(interleave(None)))
- assert raises(TypeError, lambda: list(interleave([None, None])))
- assert raises(TypeError,
- lambda: list(interleave([[1, 2], GenException(ValueError)],
- pass_exceptions=None)))
- tested.append('interleave')
- assert raises(TypeError, lambda: list(interpose(1, None)))
- tested.append('interpose')
- assert raises(TypeError, lambda: isdistinct(None))
- tested.append('isdistinct')
- assert isiterable(None) is False
- tested.append('isiterable')
- assert raises(TypeError, lambda: list(iterate(None, 1)))
- tested.append('iterate')
- assert raises(TypeError, lambda: last(None))
- tested.append('last')
- # XXX
- assert (raises(TypeError, lambda: list(mapcat(None, [[1], [2]]))) or
- list(mapcat(None, [[1], [2]])) == [[1], [2]])
- assert raises(TypeError, lambda: list(mapcat(identity, [None, [2]])))
- assert raises(TypeError, lambda: list(mapcat(identity, None)))
- tested.append('mapcat')
- assert raises(TypeError, lambda: list(merge_sorted(None, [1, 2])))
- tested.append('merge_sorted')
- assert raises(TypeError, lambda: nth(None, [1, 2]))
- assert raises(TypeError, lambda: nth(0, None))
- tested.append('nth')
- assert raises(TypeError, lambda: partition(None, [1, 2, 3]))
- assert raises(TypeError, lambda: partition(1, None))
- tested.append('partition')
- assert raises(TypeError, lambda: list(partition_all(None, [1, 2, 3])))
- assert raises(TypeError, lambda: list(partition_all(1, None)))
- tested.append('partition_all')
- assert raises(TypeError, lambda: list(pluck(None, [[1], [2]])))
- assert raises(TypeError, lambda: list(pluck(0, [None, [2]])))
- assert raises(TypeError, lambda: list(pluck(0, None)))
- tested.append('pluck')
- assert raises(TypeError, lambda: reduceby(None, add, [1, 2, 3], 0))
- assert raises(TypeError, lambda: reduceby(identity, None, [1, 2, 3], 0))
- assert raises(TypeError, lambda: reduceby(identity, add, None, 0))
- tested.append('reduceby')
- assert raises(TypeError, lambda: list(remove(None, [1, 2])))
- assert raises(TypeError, lambda: list(remove(identity, None)))
- tested.append('remove')
- assert raises(TypeError, lambda: second(None))
- tested.append('second')
- # XXX
- assert (raises(TypeError, lambda: list(sliding_window(None, [1, 2, 3]))) or
- list(sliding_window(None, [1, 2, 3])) == [])
- assert raises(TypeError, lambda: list(sliding_window(1, None)))
- tested.append('sliding_window')
- # XXX
- assert (raises(TypeError, lambda: list(take(None, [1, 2])) == [1, 2]) or
- list(take(None, [1, 2])) == [1, 2])
- assert raises(TypeError, lambda: list(take(1, None)))
- tested.append('take')
- # XXX
- assert (raises(TypeError, lambda: list(tail(None, [1, 2])) == [1, 2]) or
- list(tail(None, [1, 2])) == [1, 2])
- assert raises(TypeError, lambda: list(tail(1, None)))
- tested.append('tail')
- # XXX
- assert (raises(TypeError, lambda: list(take_nth(None, [1, 2]))) or
- list(take_nth(None, [1, 2])) == [1, 2])
- assert raises(TypeError, lambda: list(take_nth(1, None)))
- tested.append('take_nth')
- assert raises(TypeError, lambda: list(unique(None)))
- assert list(unique([1, 1, 2], key=None)) == [1, 2]
- tested.append('unique')
- assert raises(TypeError, lambda: join(first, None, second, (1, 2, 3)))
- assert raises(TypeError, lambda: join(first, (1, 2, 3), second, None))
- tested.append('join')
- assert raises(TypeError, lambda: topk(None, [1, 2, 3]))
- assert raises(TypeError, lambda: topk(3, None))
- tested.append('topk')
- assert raises(TypeError, lambda: list(diff(None, [1, 2, 3])))
- assert raises(TypeError, lambda: list(diff(None)))
- assert raises(TypeError, lambda: list(diff([None])))
- assert raises(TypeError, lambda: list(diff([None, None])))
- tested.append('diff')
- assert raises(TypeError, lambda: peek(None))
- tested.append('peek')
- assert raises(TypeError, lambda: peekn(None, [1, 2, 3]))
- assert raises(TypeError, lambda: peekn(3, None))
- tested.append('peekn')
- assert raises(TypeError, lambda: list(random_sample(None, [1])))
- assert raises(TypeError, lambda: list(random_sample(0.1, None)))
- tested.append('random_sample')
- s1 = set(tested)
- s2 = set(cytoolz.itertoolz.__all__)
- assert s1 == s2, '%s not tested for being None-safe' % ', '.join(s2 - s1)
- def test_recipes():
- tested = []
- # XXX
- assert (raises(TypeError, lambda: countby(None, [1, 2])) or
- countby(None, [1, 2]) == {(1,): 1, (2,): 1})
- assert raises(TypeError, lambda: countby(identity, None))
- tested.append('countby')
- # XXX
- assert (raises(TypeError, lambda: list(partitionby(None, [1, 2]))) or
- list(partitionby(None, [1, 2])) == [(1,), (2,)])
- assert raises(TypeError, lambda: list(partitionby(identity, None)))
- tested.append('partitionby')
- s1 = set(tested)
- s2 = set(cytoolz.recipes.__all__)
- assert s1 == s2, '%s not tested for being None-safe' % ', '.join(s2 - s1)
|