Repository: enthought/traits-futures (language: python)

Issue:
Regression: instantiating a future directly fails
In Traits Futures 0.2.0, it's possible to instantiate a `CallFuture` (for example) directly:

```python
>>> from traits_futures.api import CallFuture
>>> CallFuture()
<traits_futures.background_call.CallFuture object at 0x106a22860>
```

But in Traits Futures 0.3.0, this fails:

```python
>>> from traits_futures.api import CallFuture
>>> CallFuture()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/mdickinson/.venvs/traits-futures/lib/python3.9/site-packages/traits/has_traits.py", line 3474, in __init__
    raise TraitError(
traits.trait_errors.TraitError: The following required traits were not provided: _cancel.
```

Instantiating futures directly is not something that's really supported, but it's currently being used in the test suite of at least one project. To avoid gratuitous breakage, we should consider reverting the use of `HasRequiredTraits` here.



Discussion / hints:

Fix patch:
diff --git a/docs/source/changes.rst b/docs/source/changes.rst
index f5108030..d47d7ce0 100644
--- a/docs/source/changes.rst
+++ b/docs/source/changes.rst
@@ -10,6 +10,18 @@
    Thanks for using Enthought open source!
 
 
+Release 0.3.1
+-------------
+
+Release date: XXXX-XX-XX
+
+Fixes
+~~~~~
+
+* Fix regression where |BaseFuture| subclasses could not be instantiated
+  without any arguments. (#467)
+
+
 Release 0.3.0
 -------------
 
diff --git a/traits_futures/base_future.py b/traits_futures/base_future.py
index 578cbc7d..47c02428 100644
--- a/traits_futures/base_future.py
+++ b/traits_futures/base_future.py
@@ -20,7 +20,7 @@
     Bool,
     Callable,
     Enum,
-    HasRequiredTraits,
+    HasStrictTraits,
     observe,
     Property,
     Str,
@@ -142,7 +142,7 @@ class _StateTransitionError(Exception):
 
 
 @IFuture.register
-class BaseFuture(HasRequiredTraits):
+class BaseFuture(HasStrictTraits):
     """
     Convenience base class for the various flavours of Future.
     """
@@ -460,7 +460,7 @@ def _user_cancelled(self):
 
     #: Callback called (with no arguments) when user requests cancellation.
     #: This is reset to ``None`` once cancellation is impossible.
-    _cancel = Callable(allow_none=True, required=True)
+    _cancel = Callable(allow_none=True)
 
     #: The internal state of the future.
     _internal_state = Enum(WAITING, list(_INTERNAL_STATE_TO_STATE))


Test patch:
diff --git a/traits_futures/tests/common_future_tests.py b/traits_futures/tests/common_future_tests.py
index 1141b282..788b7735 100644
--- a/traits_futures/tests/common_future_tests.py
+++ b/traits_futures/tests/common_future_tests.py
@@ -199,6 +199,11 @@ def test_interface(self):
         future = self.future_class(_cancel=dummy_cancel_callback)
         self.assertIsInstance(future, IFuture)
 
+    def test_zero_argument_instantiation(self):
+        # Regression test for enthought/traits-futures#466
+        future = self.future_class()
+        self.assertIsInstance(future, IFuture)
+
     def send_message(self, future, message, cancel_callback):
         """Send a particular message to a future."""
         if message == "A":