boot-scootin
boot-scootin

Reputation: 12515

Assertions and testing emptiness of iterables

Using the built-in assert statement, is there a good, Pythonic way of checking emptiness in an iterable? I have seen:

but I'm looking for a solution using assertions. The following looks to work, but I am unsure whether I'm missing some important possible exception:

a = [1, 2, 3]
b = []

assert a, 'a is empty'
assert b, 'b is empty'

Which raises this AssertionError:

Traceback (most recent call last):
  File "<ipython-input-9-185714f6eb3c>", line 5, in <module>
    assert b, 'b is empty'
AssertionError: b is empty

Upvotes: 0

Views: 1056

Answers (2)

jez
jez

Reputation: 15359

How about:

assert len(a), "a is empty"

It has the same effect as assert a, ... for list, dict and set objects but you may find that it looks clearer/more maintainable.

Neither assert a, ... nor assert len(a), ... is guaranteed to work for arbitrary iterables, though you may find the latter preferable simply because it throws an exception for iterables that have no __len__, rather than potentially letting the assertion pass with a false-positive. It looks like there is no completely general solution (i.e. a solution for which you couldn't construct an iterable that would defeat it).

Upvotes: -2

user2722968
user2722968

Reputation: 16535

Remember not to confuse a container (think lists, sets, tuples, dictionaries, whatever) with an iterable (anything that can come up with a new state). The only way to check whether an iterable is "empty" is to try to iterate over it and find out if it does not produce at least one new state. You are free to interpret that as the iterable being "empty".

Something like this

def test_thing_is_empty(self):  # We are in a unittest.Testcase
    foo = iter([])  # Or any other iterable thing
    try:
        item = next(foo)
    except StopIteration:
        pass  # This is what should happen
    else:
        self.fail("Expected foo to be empty, got %s" % (item, ))

Also remember that an iterable is technically never truly "empty". It is allowed, though discouraged, for an iterable to throw a StopIteration sometimes and all of the sudden start producing new elements if tried again.

Therefore there is no way to assert this. You have to try and see if you fail. If you do fail, the only true knowledge is that the iterable did not produce a new state right now (for whatever reason). If you get an item back, the iterable just produced a unique, precious snowflake that may never be reproducable again (think reading from network socket somewhere down the stack).

Upvotes: 4

Related Questions