Adds a __repr__ to graph.
[pyutils.git] / src / pyutils / parallelize / smart_future.py
index 310560db21e06459c728380a402e583571aebd0c..6da4db2da1c042030503903f6e9434f1b4e41494 100644 (file)
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# © Copyright 2021-2022, Scott Gasch
+# © Copyright 2021-2023, Scott Gasch
 
 """
 A :class:`Future` that can be treated as a substutute for the result
@@ -10,6 +10,8 @@ the internal result actually becomes available.
 
 Results from :class:`parallelize.parallelize` are returned wrapped
 in :class:`SmartFuture` instances.
+
+Also contains some utilility code for waiting for one/many futures.
 """
 
 from __future__ import annotations
@@ -28,7 +30,7 @@ from pyutils.parallelize.deferred_operand import DeferredOperand
 
 logger = logging.getLogger(__name__)
 
-T = TypeVar('T')
+T = TypeVar("T")
 
 
 def wait_any(
@@ -48,7 +50,7 @@ def wait_any(
             futures completes
         log_exceptions: Should we log (warning + exception) any
             underlying exceptions raised during future processing or
-            silently ignore then?
+            silently ignore them?
         timeout: invoke callback with a periodicity of timeout while
             awaiting futures
 
@@ -77,14 +79,14 @@ def wait_any(
                 if log_exceptions and not f.cancelled():
                     exception = f.exception()
                     if exception is not None:
-                        logger.warning(
-                            'Future 0x%x raised an unhandled exception and exited.',
+                        logger.exception(
+                            "Future 0x%x raised an unhandled exception and exited.",
                             id(f),
+                            exc_info=exception,
                         )
-                        logger.exception(exception)
                         raise exception
                 yield smart_future_by_real_future[f]
-        except TimeoutError:
+        except concurrent.futures.TimeoutError:
             if callback is not None:
                 callback()
     if callback is not None:
@@ -103,7 +105,7 @@ def wait_all(
         futures: A collection of futures that we're waiting for
         log_exceptions: Should we log (warning + exception) any
             underlying exceptions raised during future processing or
-            silently ignore then?
+            silently ignore them?
 
     Returns:
         Only when all futures in the input list are ready.  Blocks
@@ -123,10 +125,11 @@ def wait_all(
             if not f.cancelled():
                 exception = f.exception()
                 if exception is not None:
-                    logger.warning(
-                        'Future 0x%x raised an unhandled exception and exited.', id(f)
+                    logger.exception(
+                        "Future 0x%x raised an unhandled exception and exited.",
+                        id(f),
+                        exc_info=exception,
                     )
-                    logger.exception(exception)
                     raise exception
     assert len(done) == len(real_futures)
     assert len(not_done) == 0
@@ -147,10 +150,14 @@ class SmartFuture(DeferredOperand):
             wrapped_future: a normal Python :class:`concurrent.Future`
                 object that we are wrapping.
         """
+        super().__init__(set(["id", "wrapped_future", "get_id", "is_ready"]))
         assert isinstance(wrapped_future, fut.Future)
         self.wrapped_future = wrapped_future
         self.id = id_generator.get("smart_future_id")
 
+        # Note: if you are adding any settable properties to this
+        # class, go add them to the set in DeferredOperand.__setattr__()
+
     def get_id(self) -> int:
         """
         Returns: