Skip to content

Instantly share code, notes, and snippets.

@erikbern
Last active July 21, 2022 16:32
Show Gist options
  • Save erikbern/01ae78d15f89edfa7f77e5c0a827a94d to your computer and use it in GitHub Desktop.
Save erikbern/01ae78d15f89edfa7f77e5c0a827a94d to your computer and use it in GitHub Desktop.
Output a warning if a generator function is called but never consumed
# Let's say you have a method `map(f, inputs)` that performs some operation on inputs
# Let's also sayu that this function returns a generator.
# A user might pass a function f that has some side effect, e.g. `def f(x): print(x)`
# This might lead to confusing results if the output is never consumed:
# `map(f, inputs)` -> this does nothing, can be confusing to the user
# `list(map(f, inputs))` -> this executes `f` on all inputs
# To remedy the situation, we can provide a helpful warning to the user if the generator
# is never consumed.
### Helper code:
import functools
import warnings
class GeneratorWrapper:
def __init__(self, gen, f):
self.gen = gen
self.f = f
self.iterated = False
def __iter__(self):
self.iterated = True
return self.gen
def __del__(self):
if not self.iterated:
warnings.warn(f"Function `{self.f.__name__}` returned generator that was never consumed", UserWarning)
def warn_if_not_consumed(f):
@functools.wraps(f)
def f_wrapped(*args, **kwargs):
gen = f(*args, **kwargs)
return GeneratorWrapper(gen, f)
return f_wrapped
### Example usage:
@warn_if_not_consumed
def generator(n):
for i in range(n):
yield i
# This doesn't warn!
for x in generator(3):
print(x)
# This will warn!
generator(5)
@erikbern
Copy link
Author

Yeah, I agree that this could be extended to handle partial consumption too

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment