Adds a __repr__ to graph.
[pyutils.git] / src / pyutils / parallelize / parallelize.py
index 515d431dea4e4fa8c7c4101a0573d4308990c0e5..3cf1f6abc93305660b13a1c252bc8cf06c708a4c 100644 (file)
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# © Copyright 2021-2022, Scott Gasch
+# © Copyright 2021-2023, Scott Gasch
 
 """A decorator to help with simple parallelization.  When decorated
 functions are invoked they execute on a background thread, process or
@@ -12,10 +12,12 @@ remote machine depending on the style of decoration::
     def my_function(a, b, c) -> int:
         ...do some slow / expensive work, e.g., an http request
 
+    # Run with background subprocess
     @p.parallelize(method=Method.PROCESS)
     def my_other_function(d, e, f) -> str:
         ...do more really expensive work, e.g., a network read
 
+    # Run in a helper process on another machine.
     @p.parallelize(method=Method.REMOTE)
     def my_other_other_function(g, h) -> int:
         ...this work will be distributed to a remote machine pool
@@ -72,6 +74,33 @@ do some setup work:
     If you're trying to set this up and struggling, email me at
     [email protected].  I'm happy to help.
 
+    What you get back when you call a decorated function (using
+    threads, processes or a remote worker) is a
+    :class:`pyutils.parallelize.smart_future.SmartFuture`.  This class
+    attempts to transparently wrap a normal Python :class:`Future`
+    (see
+    https://docs.python.org/3/library/concurrent.futures.html#future-objects).
+    If your code just uses the result of a `parallelized` method it
+    will block waiting on the result of the wrapped function as soon
+    as it uses that result in a manner that requires its value to be
+    known (e.g. using it in an expression, calling a method on it,
+    passing it into a method, hashing it / using it as a dict key,
+    etc...)  But you can do operations that do not require the value
+    to be known (e.g. storing it in a list, storing it as a value in a
+    dict, etc...) safely without blocking.
+
+    There are two helper methods in
+    :mod:`pyutils.parallelize.smart_future` to help deal with these
+    :class:`SmartFuture` objects called
+    :meth:`pyutils.parallelize.smart_future.wait_all` and
+    :meth:`pyutils.parallelize.smart_future.wait_any`.  These, when
+    given a collection of :class:`SmartFuture` objects,
+    will block until one (any) or all (all) are finished and yield the
+    finished objects to the caller.  Callers can be confident that any
+    objects returned from these methods will not block when accessed.
+    See documentation in :mod:`pyutils.parallelize.smart_future` for
+    more details.
+
 """
 
 import atexit
@@ -102,10 +131,12 @@ def parallelize(
         def my_function(a, b, c) -> int:
             ...do some slow / expensive work, e.g., an http request
 
+        # Run with background subprocess
         @p.parallelize(method=Method.PROCESS)
         def my_other_function(d, e, f) -> str:
             ...do more really expensive work, e.g., a network read
 
+        # Run in a helper process on another machine.
         @p.parallelize(method=Method.REMOTE)
         def my_other_other_function(g, h) -> int:
             ...this work will be distributed to a remote machine pool