New submission from Brian McCutchon <bmccutc...@google.com>:

Currently, it is possible to make a basic single-threaded executor for unit 
testing:

class FakeExecutor(futures.Executor):

  def submit(self, f, *args, **kwargs):
    future = futures.Future()
    future.set_result(f(*args, **kwargs))
    return future

  def shutdown(self, wait=True):
    pass

However, this evaluates the provided function eagerly, which may be undesirable 
for tests. It prevents the tests from catching a whole class of errors (those 
where the caller forgot to call .result() on a future that is only desirable 
for its side-effects). It would be great to have an Executor implementation 
where the function is only called when .result() is called so tests can catch 
those errors.

I might add that, while future.set_result is documented as being supported for 
unit tests, a comment in the CPython source says that Future.__init__() "Should 
not be called by clients" 
(https://github.com/python/cpython/blob/master/Lib/concurrent/futures/_base.py#L317),
 suggesting that even the above code is unsupported and leaving me wondering 
how I should test future-heavy code without using mock.patch on everything.

------ Alternatives that don't work ------

One might think it possible to create a FakeFuture to do this:

class FakeFuture(object):

  def __init__(self, to_invoke):
    self._to_invoke = to_invoke

  def result(self, timeout=None):
    return self._to_invoke()

However, futures.wait is not happy with this:

futures.wait([FakeFuture(lambda x: 1)])  # AttributeError: 'FakeFuture' object 
has no attribute '_condition'

If FakeFuture is made to extend futures.Future, the above line instead hangs:

class FakeFuture(futures.Future):

  def __init__(self, to_invoke):
    super(FakeFuture, self).__init__()
    self._to_invoke = to_invoke

  def result(self, timeout=None):
    return self._to_invoke()

I feel like I shouldn't have to patch out wait() in order to get good unit 
tests.

----------
messages: 338576
nosy: Brian McCutchon
priority: normal
severity: normal
status: open
title: Add deferred single-threaded/fake executor to concurrent.futures
type: enhancement
versions: Python 3.7

_______________________________________
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue36395>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to