Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-curio for openSUSE:Factory 
checked in at 2021-09-03 21:25:44
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-curio (Old)
 and      /work/SRC/openSUSE:Factory/.python-curio.new.1899 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-curio"

Fri Sep  3 21:25:44 2021 rev:9 rq:914807 version:1.5

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-curio/python-curio.changes        
2020-12-12 20:31:37.417805670 +0100
+++ /work/SRC/openSUSE:Factory/.python-curio.new.1899/python-curio.changes      
2021-09-03 21:26:32.854200265 +0200
@@ -1,0 +2,12 @@
+Tue Aug 24 07:52:53 UTC 2021 - John Paul Adrian Glaubitz 
<adrian.glaub...@suse.com>
+
+- Update to 1.5:
+  * Fixed Issue #340 related to the handling of daemonic tasks
+    in a TaskGroup.
+  * Changed all cancellation related exceptions to inherit
+    from BaseException instead of Exception.
+  * Added ps() and where() commands to the monitor
+    that can be used from the Curio REPL when you run
+    `python -m curio`.
+
+-------------------------------------------------------------------

Old:
----
  curio-1.4.tar.gz

New:
----
  curio-1.5.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-curio.spec ++++++
--- /var/tmp/diff_new_pack.Ld8FKi/_old  2021-09-03 21:26:33.290200720 +0200
+++ /var/tmp/diff_new_pack.Ld8FKi/_new  2021-09-03 21:26:33.290200720 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-curio
 #
-# Copyright (c) 2020 SUSE LLC
+# Copyright (c) 2021 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %define skip_python2 1
 Name:           python-curio
-Version:        1.4
+Version:        1.5
 Release:        0
 Summary:        Concurrent I/O library for Python 3
 License:        BSD-Source-Code

++++++ curio-1.4.tar.gz -> curio-1.5.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.4/CHANGES new/curio-1.5/CHANGES
--- old/curio-1.4/CHANGES       2020-08-24 11:41:46.000000000 +0200
+++ new/curio-1.5/CHANGES       2021-03-11 16:52:49.000000000 +0100
@@ -1,9 +1,58 @@
 CHANGES
 -------
+Version 1.5 - In Progress
+------------------------------
+03/10/2021 Fixed Issue #340 related to the handling of daemonic tasks
+           in a TaskGroup.   TaskGroups can now be given existing
+           daemonic tasks in the constructor.  Daemonic tasks are
+           correctly cancelled if a TaskGroup used as a context manager
+           terminates due to an exception.
+           
+10/25/2020 Changed all cancellation related exceptions to inherit
+           from BaseException instead of Exception.   This makes
+           them play slightly better with normal exception handling
+           code:
+
+               try:
+                   await some_operation()
+               except Exception as err:
+                   # Normal (expected) program related error
+                   ...
+               except CancelledError as err:
+                   # Cancellation of some kind
+                   ...
+
+           This also mirrors a similar change to asyncio.CancelledError
+           which now directly inherits from BaseException.
+
+           ***POTENTIAL INCOMPATIBILITY***
+
+           If you were using try: ... except Exception: to catch
+           cancellation, that code will break. 
+           
+10/17/2020 Added ps() and where() commands to the monitor
+           that can be used from the Curio REPL when you run
+           `python -m curio`.  These can also be used to monitor
+           the state of the kernel from a different thread.
+           For example:
+
+               from threading import Thread
+               
+               kern = Kernel()
+               Thread(target=kern.run, args=[main]).start()
+
+               >>> from curio.monitor import ps
+               >>> ps(kern)
+               ... displays active tasks ...
+
+           This makes it a bit easier to have some of the monitor
+           capability in an live-environment where Curio is running.
+
 Version 1.4 - August 23, 2020
 -----------------------------
 08/23/2020 Fixed minimum requirement in setup.py
 
+
 Version 1.3 - August 23, 2020
 -----------------------------
 08/21/2020 Moved the Pytest plugin to the examples directory.  There have 
@@ -11,81 +60,81 @@
            installed by default.  It was never used by Curio itself.
 
 06/16/2020 Refined the detection of coroutines to use 
collections.abc.Coroutine.
-          This change should not affect any existing part of Curio, but it
-          allows it to properly recognize async functions defined in
-          extensions such as Cython.  See Issue #326.
-          
+           This change should not affect any existing part of Curio, but it
+           allows it to properly recognize async functions defined in
+           extensions such as Cython.  See Issue #326.
+           
 06/11/2020 Added a Result object.  It's like an Event except that it has a
            an associated value/exception attached to it.  Here's the basic
-          usage pattern:
+           usage pattern:
 
                result = Result()
-              ...
-              async def do_work():
-                  try:
-                      ...
+               ...
+               async def do_work():
+                   try:
+                       ...
                        await result.set_value(value)
-                  except Exception as e:
-                      await result.set_exception(e)
+                   except Exception as e:
+                       await result.set_exception(e)
 
                async def some_task():
-                  ...
-                  try:
-                      value = await result.unwrap() 
-                      print("Success:", value)
-                  except Exception as e:
-                      print("Fail:", e)
+                   ...
+                   try:
+                       value = await result.unwrap() 
+                       print("Success:", value)
+                   except Exception as e:
+                       print("Fail:", e)
 
            In this example, the unwrap() method blocks until the result
-          becomes available.
+           becomes available.
 
 06/09/2020 Having now needed it a few projects, have added a UniversalResult
            object.  It allows Curio tasks or threads to wait for a result
-          to be set by another thread or task.   For example:
+           to be set by another thread or task.   For example:
 
                def do_work(result):
-                  ...
-                  result.set_value(value)
+                   ...
+                   result.set_value(value)
 
                async def some_task(result):
-                  ...
-                  value = await result.unwrap()
-                  ...
+                   ...
+                   value = await result.unwrap()
+                   ...
 
                result = UniversalResult()
-              threading.Thread(target=do_work, args=[result]).start()
-              curio.run(some_task, result)
+               threading.Thread(target=do_work, args=[result]).start()
+               curio.run(some_task, result)
 
            UniversalResult is somewhat similar to a Future.  However, it
-          really only allows setting and waiting.  There are no callbacks,
-          cancellation, or any other extras.
+           really only allows setting and waiting.  There are no callbacks,
+           cancellation, or any other extras.
 
 Version 1.2 - April 6, 2020
 ---------------------------
 
 04/06/2020 Removed hard dependency on contextvars.   It was unnecessary and
            needlessly broken some things on Python 3.6.
-          
+           
 04/06/2020 Added a default selector timeout of 1 second for Windows.  This
            makes everything a bit more friendly for Control-C.  This can be
-          disabled or changed using the max_select_timeout argument to Kernel
-          or curio.run().
-          
+           disabled or changed using the max_select_timeout argument to Kernel
+           or curio.run().
+           
 04/04/2020 First crack at a Curio repl.  Idea borrowed from the asyncio
            REPL.  If you run `python -m curio` it will start a REPL
-          in which Curio is already running in the background. You
-          can directly await operations at the top level. For example:
+           in which Curio is already running in the background. You
+           can directly await operations at the top level. For example:
 
               bash $ python -m curio
               Use "await" directly instead of "curio.run()".
               Type "help", "copyright", "credits" or "license" for more 
information.
               >>> import curio
               >>> await curio.sleep(4)
-             >>>
+              >>>
 
            Pressing Control-C causes any `await` operation to be cancelled
-          with a `curio.TaskCancelled` exception.   For normal operations
-          not involving `await, Control-C raises a `KeyboardInterrupt` as
+           with a `curio.TaskCancelled` exception.   For normal operations
+           not involving `await, Control-C raises a `KeyboardInterrupt` as
            normal.  Note: This feature requires Python 3.8 or newer. 
 
 03/24/2020 Added a pytest plugin for Curio. Contributed by Keith Dart.
@@ -434,7 +483,7 @@
 
 04/14/2018 Changed the method of spawning processes for run_in_process to
            use the "spawn" method of the multiprocessing module. This 
-          prevents issues of having open file-descriptors cloned by
+           prevents issues of having open file-descriptors cloned by
            accident via fork().  For example, as in Issue #256.
 
 Version 0.9 : March 10, 2018
@@ -499,7 +548,7 @@
               result = await t.join()
 
            Previously, async threads were created using the AsyncThread class.
-          That still works, but the spawn_thread() function is probably easier.
+           That still works, but the spawn_thread() function is probably 
easier.
 
            The launched function must be a normal synchronous function. 
            spawn_thread() can also be used as a context manager (see below).
@@ -535,7 +584,7 @@
            it's still needed.   Better yet, maybe take a look at PEP 567.
 
 02/02/2018 Some refactoring and simplification of the kernel run() 
-          method.  First, run() only executes as long as the
+           method.  First, run() only executes as long as the
            submitted coroutine is active.  Upon termination, run()
            immediately returns regardless of whether or not other
            tasks are still running.  Curio was already generating
@@ -573,7 +622,7 @@
            exception would be noticed there.
 
 01/22/2018 Semaphore and BoundedSemaphore now exposes a read-only
-          property ".value" that gives the current Semaphore value.
+           property ".value" that gives the current Semaphore value.
            BoundedSemaphore objects expose a ".bound" property that
            gives the upper bound.
            
@@ -597,8 +646,8 @@
 
 09/15/2017 Made the @async_thread decorator return the actual
            exception that gets raised in the event of a failure.
-          This makes it more like a normal function call.  Previously,
-          it was returning a TaskError exception for any crash.
+           This makes it more like a normal function call.  Previously,
+           it was returning a TaskError exception for any crash.
            That's a bit weird since the use of threads or spawning
            of an external task is meant to be more implicit.
 
@@ -621,19 +670,19 @@
 04/23/2017 Change to ssl.wrap_socket() function and method to make it an
            async method.   If applied to an already connected socket, it
            needs to run the handshake--which needs to be async.  See
-          Issue #206.
+           Issue #206.
 
 04/14/2017 Refinement to SignalEvent to make it work better on Windows.
            It's now triggered via the same file descriptor used for SignalQueue
            objects.
 
 03/26/2017 Added a Task.interrupt() method.  Cancels the task's current
-          blocking operation with an 'TaskInterrupted' exception.
-          This is really just a more nuanced form of cancellation,
-          similar to a timeout.  However, it's understood that an 
-          interrupted operation doesn't necessarily mean that the
-          task should quit.   Instead, the task might coordinate with 
-          other tasks in some way and retry later. 
+           blocking operation with an 'TaskInterrupted' exception.
+           This is really just a more nuanced form of cancellation,
+           similar to a timeout.  However, it's understood that an 
+           interrupted operation doesn't necessarily mean that the
+           task should quit.   Instead, the task might coordinate with 
+           other tasks in some way and retry later. 
 
 Version 0.7 : March 17, 2017
 ----------------------------
@@ -663,8 +712,8 @@
                   await g.cancel_remaining()
 
 03/12/2017 Added a .cancelled attribute to the context manager used
-          by the ignore_after() and ignore_at() functions.  It
-          can be used to determine if a timeout fired.  For example:
+           by the ignore_after() and ignore_at() functions.  It
+           can be used to determine if a timeout fired.  For example:
 
               async with ignore_after(10) as context:
                   await sleep(100)
@@ -681,7 +730,7 @@
                         ...
 
 03/08/2017 More work on signal handling. New objects: SignalQueue 
-          and SignalEvent.  SignalEvents are neat:
+           and SignalEvent.  SignalEvents are neat:
 
                import signal
                from curio import SignalEvent
@@ -693,7 +742,7 @@
                    print("Goodbye")
 
 03/08/2017 UniversalEvent object added.  An event that's safe for use in
-          Curio and threads.
+           Curio and threads.
 
 03/08/2017 Further improvement to signal handling.  Now handled by a backing
            thread.  Supports signal delivery to multiple threads, 
@@ -701,7 +750,7 @@
            of stuff.
 
 03/08/2017 Removed signal handling from the kernel up into Curio
-          "user-space".  No existing code the uses signals should break.
+           "user-space".  No existing code the uses signals should break.
 
 03/07/2017 Refined error reporting and warnings related to Task
            termination.  If any non-daemonic task is garbage collected
@@ -712,7 +761,7 @@
            crashed with an uncaught exception, that exception is
            logged as an error.
 
-          This change has a few impacts.  First, if a task crashes,
+           This change has a few impacts.  First, if a task crashes,
            but is joined, you won't get a spurious output message
            showing the crash.  The exception was delivered to someone
            else.   On the other hand, you might get more warning
@@ -720,21 +769,21 @@
            attention to their result. 
 
 03/06/2017 A lot of cleanup of the kernel. Moved some functionality 
-          elsewhere. Removed unneeded traps.  Removed excess
+           elsewhere. Removed unneeded traps.  Removed excess
            abstraction in the interest of readability.  The different
            trap functions in Curio are almost all quite small.
            However, details concerning their execution behavior was
            split across a few different places in the code and wrapped
            with decorators--making it somewhat hard to piece together
            how they worked looking at them in isolation.  Each trap 
-          is now basically self-contained.  You can look at the code and 
-          see exactly what it does.  Each trap is also afforded more
-          flexibility about how it could work in the future (e.g.,
-          scheduling behavior, cancellation, etc.).  
-
-          Debug logging features have been removed from the kernel and
-          placed into a new subsystem.  See the file curio/debug.py.
-          This is still in progress.
+           is now basically self-contained.  You can look at the code and 
+           see exactly what it does.  Each trap is also afforded more
+           flexibility about how it could work in the future (e.g.,
+           scheduling behavior, cancellation, etc.).  
+
+           Debug logging features have been removed from the kernel and
+           placed into a new subsystem.  See the file curio/debug.py.
+           This is still in progress.
 
 03/05/2017 Change to how the debugging monitor is invoked.  It's still
            an option on the run() function.  However, it is not a
@@ -749,8 +798,8 @@
 
 03/04/2017 Support for using Event.set() from a synchronous context has
            been withdrawn.   This was undocumented and experimental.
-          There are other mechanisms for achieving this.  For example,
-          communicating through a UniversalQueue.
+           There are other mechanisms for achieving this.  For example,
+           communicating through a UniversalQueue.
 
 03/03/2017 timeout_after() and related functions now accept
            coroutine functions and arguments such as this:
@@ -786,7 +835,7 @@
 
 03/03/2017 Functionality for using Queue.put() in a synchronous context
            has been withdrawn. This was always experimental and undocumented.
-          There are better alternatives for doing this.  For example, use a
+           There are better alternatives for doing this.  For example, use a
            UniversalQueue.
 
 03/01/2017 Addition of an asyncio bridge.  You can instantiate a separate
@@ -807,8 +856,8 @@
 
 02/26/2017 Modified the gather() function so that it also cancels all tasks
            if it is cancelled by timeout or other means.  See issue #186.
-          The resulting exception has a .results attribute set with
-          the results of all tasks at the time of cancellation.
+           The resulting exception has a .results attribute set with
+           the results of all tasks at the time of cancellation.
 
 02/19/2017 Added new curio.zmq module for supporting ZeroMQ.
 
@@ -828,7 +877,7 @@
 
 02/11/2017 Added a guard for proper use of asynchronous generators
            involving asynchronous finalization.  Must be wrapped by 
finalize(). 
-          For example:
+           For example:
 
            async def some_generator():
                ...
@@ -878,16 +927,16 @@
 
            async def main():
                t = await aside(child, 'Spam', 10)   # Runs in subprocess
-              await t.join()
+               await t.join()
 
            run(main())
 
            In a nutshell, aside(coro, *args, **kwargs) creates a clean
-          Python interpreter and invokes curio.run(coro(*args,
-          **kwargs)) on the supplied coroutine.  The return value of
-          aside() is a Task object.  Joining with it returns the
-          child exit code (normally 0).  Cancelling it causes a
-          TaskCancelled exception to be raised in the child.
+           Python interpreter and invokes curio.run(coro(*args,
+           **kwargs)) on the supplied coroutine.  The return value of
+           aside() is a Task object.  Joining with it returns the
+           child exit code (normally 0).  Cancelling it causes a
+           TaskCancelled exception to be raised in the child.
 
            aside() does not involve a process fork or pipe. There
            is no underlying communication between the child and parent
@@ -981,7 +1030,7 @@
            to receive the result of the threaded operation.  This
            Future is guaranteed to have the result/exception set.
 
-          Be aware that there is no way to know when the call_on_cancel 
+           Be aware that there is no way to know when the call_on_cancel 
            function might be triggered.  It might be far in the future.
            The Curio kernel might not even be running.   Thus, it's 
            generally not safe to make too many assumptions about it.
@@ -1092,15 +1141,15 @@
 01/03/2017 Added a TaskCancelled exception.  This is now what gets 
            raised when tasks are cancelled using the task.cancel()
            method.  It is a subclass of CancelledError.   This change 
-          makes CancelledError more of an abstract exception class
+           makes CancelledError more of an abstract exception class
            for cancellation.  The TaskCancelled, TaskTimeout, and 
            TimeoutCancellationError exceptions are more specific
            subclasses that indicates exactly what has happened.
 
 01/02/2017 Major reorganization of how task cancellation works. There
-          are two major parts to it.
+           are two major parts to it.
 
-          Kernel:
+           Kernel:
 
            Every task has a boolean flag "task.allow_cancel" that
            determines whether or not cancellation exceptions (which
@@ -1112,15 +1161,15 @@
            holds onto the exception indefinitely, waiting for the task
            to re-enable cancellations.  Once re-enabled, the exception
            is raised immediately the next time the task performs a 
-          blocking operation.
+           blocking operation.
 
-          Coroutines:
+           Coroutines:
 
-          From coroutines, control of the cancellation flag is
-          performed by two functions which are used as context
-          managers:
+           From coroutines, control of the cancellation flag is
+           performed by two functions which are used as context
+           managers:
 
-          To disable cancellation, use the following construct:
+           To disable cancellation, use the following construct:
 
                async def coro():
                    async with disable_cancellation():
@@ -1135,8 +1184,8 @@
            a CancelledError exception to be raised--even manually. Doing
            so causes a RuntimeError.  
 
-          To re-enable cancellation in specific regions of code, use
-          enable_cancellation() like this:
+           To re-enable cancellation in specific regions of code, use
+           enable_cancellation() like this:
 
                async def coro():
                    async with disable_cancellation():
@@ -1148,7 +1197,7 @@
 
                            if c.cancel_pending:
                                # Cancellation is pending right now. Must bail 
out.
-                              break
+                               break
 
                    await blocking_op()   # Cancellation raised here (if any)
 
@@ -1161,7 +1210,7 @@
            will never escape the block.  Instead, they turn back into
            a pending exception which can be checked as shown above.
 
-          Normally cancellations are only delivered on blocking operations.
+           Normally cancellations are only delivered on blocking operations.
            If you want to force a check, you can use check_cancellation()
            like this:
 
@@ -1234,15 +1283,15 @@
            on the CalledProcessError exception raised if there is an error.
 
 12/03/2016 Added a parentid attribute to Task instances so you can find parent
-          tasks.   Nothing else is done with this internally.
+           tasks.   Nothing else is done with this internally.
 
 12/03/2016 Withdrew the pdb and crash_handler arguments to Kernel() and the
            run() function.  Added a pdb() method to tasks that can be used
-          to enter the debugger on a crashed task.  High-level handling
-          of crashed/terminated tasks is being rethought.   The old
-          crash_handler() callback was next to useless since no useful
-          actions could be performed (i.e., there was no ability to respawn
-          tasks or execute any kind of coroutine in response to a crash).
+           to enter the debugger on a crashed task.  High-level handling
+           of crashed/terminated tasks is being rethought.   The old
+           crash_handler() callback was next to useless since no useful
+           actions could be performed (i.e., there was no ability to respawn
+           tasks or execute any kind of coroutine in response to a crash).
 
 11/05/2016 Pulled time related functionality into the kernel as a new call.
            Use the following to get the current value of the kernel clock:
@@ -1271,7 +1320,7 @@
            
 10/29/2016 Modified TaskTimeout exception so that it subclasses CancelledError.
            This makes it easier to write code that handles any kind of 
-          cancellation (explicit cancellation, cancellation by timeout, etc.)
+           cancellation (explicit cancellation, cancellation by timeout, etc.)
 
 10/17/2016 Added shutdown() method to sockets.  It is an async function
            to mirror async implementation of close()
@@ -1417,7 +1466,7 @@
                    print('Going away because of:', e)
 
            KernelExit by itself does not do anything to other 
-          running tasks.  However, the run() function will
+           running tasks.  However, the run() function will
            separately issue a shutdown request causing all
            remaining tasks to cancel.
 
@@ -1553,7 +1602,7 @@
               s = await Spam()
 
 05/15/2016 Fixed Issue #50. Undefined variable n in io.py 
-          Reported by Wolfgang Langner
+           Reported by Wolfgang Langner
 
 Version 0.4 : May 13, 2016
 --------------------------
@@ -1567,7 +1616,7 @@
 
 05/13/2016 Modification to the abide() function to allow it to work
            with RLocks from the threading module.  One caveat: Such
-          locks are NOT reentrant from within curio itself. 
+           locks are NOT reentrant from within curio itself. 
 
 Version 0.2 : May 11, 2016
 --------------------------
@@ -1581,8 +1630,8 @@
 
 04/22/2016 Removed parent/child task relationship and associated
            tracking.  It's an added complexity that's not really
-          needed in the kernel and it can be done easily enough by
-          the user in cases where it might be needed.
+           needed in the kernel and it can be done easily enough by
+           the user in cases where it might be needed.
 
 04/18/2016 Major refactoring of timeout handling.  Virtually all
            operations in curio support cancellation and timeouts.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.4/CONTRIBUTING.md 
new/curio-1.5/CONTRIBUTING.md
--- old/curio-1.4/CONTRIBUTING.md       2020-08-24 11:41:46.000000000 +0200
+++ new/curio-1.5/CONTRIBUTING.md       2021-03-11 16:52:49.000000000 +0100
@@ -1,6 +1,7 @@
 Contributing to Curio
 =====================
 
-As a general rule, The Curio project does not accept pull requests
-except by invitation.  However, if you have found a bug, a typo, or
-have ideas for improvement, please submit an issue instead.
+Although Curio is made available as open-source software, it is not
+developed as a community project.  Thus, pull requests are not
+accepted except by invitation. However, if you have found a bug, a
+typo, or have an idea for improvement, please submit an issue instead.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.4/README.rst new/curio-1.5/README.rst
--- old/curio-1.4/README.rst    2020-08-24 11:41:46.000000000 +0200
+++ new/curio-1.5/README.rst    2021-03-11 16:52:49.000000000 +0100
@@ -2,9 +2,40 @@
 =====
 
 Curio is a coroutine-based library for concurrent Python systems
-programming.  It provides standard programming abstractions such as as
-tasks, sockets, files, locks, and queues. It works on Unix and Windows. 
-You'll find it to be familiar, small, fast, and fun
+programming using async/await.  It provides standard programming
+abstractions such as as tasks, sockets, files, locks, and queues as
+well as some advanced features such as support for structured
+concurrency. It works on Unix and Windows and has zero dependencies.
+You'll find it to be familiar, small, fast, and fun.
+
+Curio is Different
+------------------
+One of the most important ideas from software architecture is the
+"separation of concerns."  This can take many forms such as utilizing
+abstraction layers, object oriented programming, aspects, higher-order
+functions, and so forth.  However, another effective form of it exists
+in the idea of separating execution environments.  For example, "user
+mode" versus "kernel mode" in operating systems.  This is the
+underlying idea in Curio, but applied to "asynchronous" versus
+"synchronous" execution.
+
+A fundamental problem with asynchronous code is that it involves a
+completely different evaluation model that doesn't compose well with
+ordinary applications or with other approaches to concurrency such as
+thread programing.  Although the addition of "async/await" to Python
+helps clarify such code, "async" libraries still tend to be a confused
+mess of functionality that mix asynchronous and synchronous
+functionality together in the same environment--often bolting it all
+together with an assortment of hacks that try to sort out all of
+associated API confusion.
+
+Curio strictly separates asynchronous code from synchronous code.
+Specifically, *all* functionality related to the asynchronous
+environment utilizes "async/await" features and syntax--without
+exception.  Moreover, interactions between async and sync code is
+carefully managed through a small set of simple mechanisms such as
+events and queues.  As a result, Curio is small, fast, and
+significantly easier to reason about.
 
 A Simple Example
 -----------------
@@ -66,8 +97,8 @@
 ----------------------
 
 Concepts related to Curio's design and general issues related to async
-programming have been described by Curio's creator in various
-conference talks and tutorials:
+programming have been described by Curio's creator, `David Beazley 
<https://www.dabeaz.com>`_, in
+various conference talks and tutorials:
 
 * `Build Your Own Async <https://www.youtube.com/watch?v=Y4Gt3Xjd7G8>`_, 
Workshop talk by David Beazley at PyCon India, 2019.
 
@@ -96,7 +127,14 @@
 A: No. Although Curio provides a significant amount of overlapping
 functionality, the API is different.  Compatibility with other
 libaries is not a goal.
- 
+
+**Q: Is Curio meant to be compatible with other async libraries?**
+
+A: No. Curio is a stand-alone project that emphasizes a certain
+software architecture based on separation of environments.  Other
+libraries have largely ignored this concept, preferring to simply
+provide variations on the existing approach found in asyncio.
+
 **Q: Can Curio interoperate with other event loops?**
 
 A: It depends on what you mean by the word "interoperate."  Curio's
@@ -106,27 +144,30 @@
 
 **Q: How fast is Curio?**
 
-A: In rough benchmarking of the simple echo server shown here, Curio
-runs about 90% faster than comparable code using coroutines in
-``asyncio``. This was last measured on Linux using Python 3.7b3. Keep
-in mind there is a lot more to overall application performance than
-the performance of a simple echo server so your mileage might
-vary. See the ``examples/benchmark`` directory for various testing
-programs.
+A: Curio's primary goal is to be an async library that is minimal and
+understandable. Performance is not the primary concern.  That said, in
+rough benchmarking of a simple echo server, Curio is more than twice
+as fast as comparable code using coroutines in ``asyncio`` or
+``trio``.  This was last measured on OS-X using Python 3.9.  Keep in
+mind there is a lot more to overall application performance than the
+performance of a simple echo server so your mileage might
+vary. However, as a runtime environment, Curio doesn't introduce a lot of
+extra overhead. See the ``examples/benchmark`` directory for various
+testing programs.
 
 **Q: What is the future of Curio?**
 
-A: Curio should be viewed as a library of primitives related to
-concurrent systems programming.  At this time, it is considered
-to be feature-complete--meaning that it is not expected to
-sprout new capabilities.  It may be updated from time to time to
-fix bugs or support new versions of Python.
+A: Curio should be viewed as a library of basic programming
+primitives.  At this time, it is considered to be
+feature-complete--meaning that it is not expected to sprout many new
+capabilities.  It may be updated from time to time to fix bugs or
+support new versions of Python.
 
 **Q: Can I contribute?**
 
 A: Curio is not a community-based project seeking developers
 or maintainers.  However, having it work reliably is important. If you've
-found a bug or have an idea for making it better, please feel
+found a bug or have an idea for making it better, please 
 file an `issue <https://github.com/dabeaz/curio>`_. 
 
 Contributors
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.4/curio/__init__.py 
new/curio-1.5/curio/__init__.py
--- old/curio-1.4/curio/__init__.py     2020-08-24 11:41:46.000000000 +0200
+++ new/curio-1.5/curio/__init__.py     2021-03-11 16:52:49.000000000 +0100
@@ -1,6 +1,6 @@
 # curio/__init__.py
 
-__version__ = '1.4'
+__version__ = '1.5'
 
 from .errors import *
 from .queue import *
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.4/curio/__main__.py 
new/curio-1.5/curio/__main__.py
--- old/curio-1.4/curio/__main__.py     2020-08-24 11:41:46.000000000 +0200
+++ new/curio-1.5/curio/__main__.py     2021-03-11 16:52:49.000000000 +0100
@@ -1,5 +1,6 @@
 import ast
 import curio
+import curio.monitor
 import code
 import inspect
 import sys
@@ -104,7 +105,10 @@
         console.requests.put(None)
     
 if __name__ == '__main__':
-    repl_locals = { 'curio': curio }
+    repl_locals = { 'curio': curio,
+                    'ps': curio.monitor.ps,
+                    'where': curio.monitor.where,
+    }
     for key in {'__name__', '__package__',
                 '__loader__', '__spec__',
                 '__builtins__', '__file__'}:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.4/curio/channel.py 
new/curio-1.5/curio/channel.py
--- old/curio-1.4/curio/channel.py      2020-08-24 11:41:46.000000000 +0200
+++ new/curio-1.5/curio/channel.py      2021-03-11 16:52:49.000000000 +0100
@@ -119,7 +119,7 @@
             await self._writer.write(msg)
         return size
 
-    async def recv_bytes(self,maxlength=None):
+    async def recv_bytes(self, maxlength=None):
         '''
         Receive a message of bytes as a single message.
         '''
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.4/curio/errors.py 
new/curio-1.5/curio/errors.py
--- old/curio-1.4/curio/errors.py       2020-08-24 11:41:46.000000000 +0200
+++ new/curio-1.5/curio/errors.py       2021-03-11 16:52:49.000000000 +0100
@@ -13,11 +13,11 @@
 
 class CurioError(Exception):
     '''
-    Base class for all Curio-related exceptions
+    Base class for all non-cancellation Curio-related exceptions
     '''
 
 
-class CancelledError(CurioError):
+class CancelledError(BaseException):
     '''
     Base class for all task-cancellation related exceptions
     '''
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.4/curio/kernel.py 
new/curio-1.5/curio/kernel.py
--- old/curio-1.4/curio/kernel.py       2020-08-24 11:41:46.000000000 +0200
+++ new/curio-1.5/curio/kernel.py       2021-03-11 16:52:49.000000000 +0100
@@ -135,8 +135,7 @@
             raise RuntimeError("Can't run a kernel that's been shut down or 
crashed. Create a new kernel.")
 
         coro = meta.instantiate_coroutine(corofunc, *args) if corofunc else 
None
-
-        with meta.running():
+        with meta.running(self):
             # Make the kernel runtime environment (if needed)
             if not self._runner:
                 self._runner = self._make_kernel_runtime()
@@ -754,7 +753,7 @@
                                 active.exception = e
                                 if (active != main_task and not isinstance(e, 
(CancelledError, SystemExit))):
                                     log.error('Task Crash: %r', active, 
exc_info=True)
-                                if not isinstance(e, Exception):
+                                if not isinstance(e, (Exception, 
CancelledError)):
                                     raise
                             break
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.4/curio/meta.py new/curio-1.5/curio/meta.py
--- old/curio-1.4/curio/meta.py 2020-08-24 11:41:46.000000000 +0200
+++ new/curio-1.5/curio/meta.py 2021-03-11 16:52:49.000000000 +0100
@@ -32,15 +32,17 @@
 # Context manager that is used when the kernel is executing.
 
 @contextmanager
-def running():
+def running(kernel):
     if getattr(_locals, 'running', False):
         raise RuntimeError('Only one Curio kernel per thread is allowed')
     _locals.running = True
+    _locals.kernel = kernel
     try:
         with asyncgen_manager():
             yield
     finally:
         _locals.running = False
+        _locals.kernel = None
 
 def curio_running():
     '''
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.4/curio/monitor.py 
new/curio-1.5/curio/monitor.py
--- old/curio-1.4/curio/monitor.py      2020-08-24 11:41:46.000000000 +0200
+++ new/curio-1.5/curio/monitor.py      2021-03-11 16:52:49.000000000 +0100
@@ -58,9 +58,11 @@
 import telnetlib
 import argparse
 import logging
+import sys
 
 # --- Curio
 from .task import spawn
+from . import meta
 from . import queue
 
 # ---
@@ -69,6 +71,47 @@
 MONITOR_HOST = '127.0.0.1'
 MONITOR_PORT = 48802
 
+# Implementation of the 'ps' command
+def ps(kernel=None, out=sys.stdout):
+    if kernel is None:
+        kernel = meta._locals.kernel
+    headers = ('Task', 'State', 'Cycles', 'Timeout', 'Sleep', 'Task')
+    widths = (6, 12, 10, 7, 7, 50)
+    sout = ''
+    for h, w in zip(headers, widths):
+        sout += '%-*s ' % (w, h)
+    sout += '\n'
+    sout += ' '.join(w * '-' for w in widths)
+    sout += '\n'
+    timestamp = time.monotonic()
+    for taskid in sorted(kernel._tasks):
+        task = kernel._tasks.get(taskid)
+        if task:
+            timeout_remaining = format(
+                (task.timeout - timestamp),
+                '0.6f')[:7] if task.timeout else 'None'
+            sleep_remaining = format(
+                (task.sleep - timestamp),
+                '0.6f')[:7] if task.sleep else 'None'
+
+            sout += '%-*d %-*s %-*d %-*s %-*s %-*s\n' % (widths[0], taskid,
+                                                         widths[1], task.state,
+                                                         widths[2], 
task.cycles,
+                                                         widths[3], 
timeout_remaining,
+                                                         widths[4], 
sleep_remaining,
+                                                         widths[5], task.name)
+    out.write(sout)
+
+# Implementation of the 'where' command
+def where(taskid, kernel=None, out=sys.stdout):
+    if kernel is None:
+        kernel = meta._locals.kernel
+    task = kernel._tasks.get(taskid)
+    if task:
+        out.write(task.traceback() + '\n')
+    else:
+        out.write('No task %d\n' % taskid)
+    
 class Monitor(object):
     '''
     Task monitor that runs concurrently to the curio kernel in a
@@ -212,37 +255,10 @@
 ''')
 
     def command_ps(self, sout):
-        headers = ('Task', 'State', 'Cycles', 'Timeout', 'Sleep', 'Task')
-        widths = (6, 12, 10, 7, 7, 50)
-        for h, w in zip(headers, widths):
-            sout.write('%-*s ' % (w, h))
-        sout.write('\n')
-        sout.write(' '.join(w * '-' for w in widths))
-        sout.write('\n')
-        timestamp = time.monotonic()
-        for taskid in sorted(self.kernel._tasks):
-            task = self.kernel._tasks.get(taskid)
-            if task:
-                timeout_remaining = format(
-                    (task.timeout - timestamp),
-                    '0.6f')[:7] if task.timeout else 'None'
-                sleep_remaining = format(
-                    (task.sleep - timestamp),
-                    '0.6f')[:7] if task.sleep else 'None'
-
-                sout.write('%-*d %-*s %-*d %-*s %-*s %-*s\n' % (widths[0], 
taskid,
-                                                                widths[1], 
task.state,
-                                                                widths[2], 
task.cycles,
-                                                                widths[3], 
timeout_remaining,
-                                                                widths[4], 
sleep_remaining,
-                                                                widths[5], 
task.name))
+        ps(self.kernel, sout)
 
     def command_where(self, sout, taskid):
-        task = self.kernel._tasks.get(taskid)
-        if task:
-            sout.write(task.traceback() + '\n')
-        else:
-            sout.write('No task %d\n' % taskid)
+        where(taskid, self.kernel, sout)
 
     def command_signal(self, sout, signame):
         if hasattr(signal, signame):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.4/curio/task.py new/curio-1.5/curio/task.py
--- old/curio-1.4/curio/task.py 2020-08-24 11:41:46.000000000 +0200
+++ new/curio-1.5/curio/task.py 2021-03-11 16:52:49.000000000 +0100
@@ -370,9 +370,12 @@
         for task in tasks:
             assert not task.taskgroup,  "Task already assigned to a task group"
             task.taskgroup = self
-            self._tasks.add(task)
+            if not task.daemon:
+                self._tasks.add(task)
             if task.terminated:
                 self._finished.append(task)
+            elif task.daemon:
+                self._daemonic.add(task)
             else:
                 self._running.add(task)
 
@@ -561,8 +564,7 @@
     async def __aexit__(self, ty, val, tb):
         if ty:
             await self.cancel_remaining()
-        else:
-            await self.join()
+        await self.join()
 
     def __aiter__(self):
         return self
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.4/docs/conf.py new/curio-1.5/docs/conf.py
--- old/curio-1.4/docs/conf.py  2020-08-24 11:41:46.000000000 +0200
+++ new/curio-1.5/docs/conf.py  2021-03-11 16:52:49.000000000 +0100
@@ -60,9 +60,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = '1.4'
+version = '1.5'
 # The full version, including alpha/beta/rc tags.
-release = '1.4'
+release = '1.5'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.4/examples/zmq_rpcclient.py 
new/curio-1.5/examples/zmq_rpcclient.py
--- old/curio-1.4/examples/zmq_rpcclient.py     2020-08-24 11:41:46.000000000 
+0200
+++ new/curio-1.5/examples/zmq_rpcclient.py     2021-03-11 16:52:49.000000000 
+0100
@@ -3,7 +3,7 @@
 import curio_zmq as zmq
 from curio import sleep, spawn
 
-from hello import fib
+from fibserve import fib
 
 async def ticker():
     n = 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.4/setup.py new/curio-1.5/setup.py
--- old/curio-1.4/setup.py      2020-08-24 11:41:46.000000000 +0200
+++ new/curio-1.5/setup.py      2021-03-11 16:52:49.000000000 +0100
@@ -14,7 +14,7 @@
       description="Curio",
       long_description=long_description,
       license="BSD",
-      version="1.4",
+      version="1.5",
       author="David Beazley",
       author_email="d...@dabeaz.com",
       maintainer="David Beazley",
@@ -25,7 +25,7 @@
       extras_require={
           'test': tests_require,
       },
-      python_requires='>= 3.6',
+      python_requires='>= 3.7',
       # This is disabled because it often causes interference with other 
testing
       # plugins people have written.  Curio doesn't use it for it's own 
testing.
       # entry_points={"pytest11": ["curio = curio.pytest_plugin"]},
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.4/tests/test_io.py 
new/curio-1.5/tests/test_io.py
--- old/curio-1.4/tests/test_io.py      2020-08-24 11:41:46.000000000 +0200
+++ new/curio-1.5/tests/test_io.py      2021-03-11 16:52:49.000000000 +0100
@@ -730,6 +730,7 @@
         await sock.recv(8)
         await sock.send(b'Msg1\nMsg2\nMsg3\n')
         await sock.close()
+        await sleep(0.1)
         await serv.cancel()
 
     async def main():
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.4/tests/test_kernel.py 
new/curio-1.5/tests/test_kernel.py
--- old/curio-1.4/tests/test_kernel.py  2020-08-24 11:41:46.000000000 +0200
+++ new/curio-1.5/tests/test_kernel.py  2021-03-11 16:52:49.000000000 +0100
@@ -491,7 +491,7 @@
         try:
             async with timeout_after(0.1):
                 await coro2()
-        except Exception as e:
+        except BaseException as e:
             assert isinstance(e, TaskTimeout)
         else:
             assert False
@@ -520,7 +520,7 @@
     async def parent():
         try:
             await timeout_after(0.1, coro2)
-        except Exception as e:
+        except BaseException as e:
             assert isinstance(e, TaskTimeout)
         else:
             assert False
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.4/tests/test_network.py 
new/curio-1.5/tests/test_network.py
--- old/curio-1.4/tests/test_network.py 2020-08-24 11:41:46.000000000 +0200
+++ new/curio-1.5/tests/test_network.py 2021-03-11 16:52:49.000000000 +0100
@@ -39,6 +39,7 @@
         results.append(('client', resp))
         results.append('client close')
         await sock.close()
+        await sleep(0.1)
         await serv.cancel()
 
     async def main():
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.4/tests/test_socket.py 
new/curio-1.5/tests/test_socket.py
--- old/curio-1.4/tests/test_socket.py  2020-08-24 11:41:46.000000000 +0200
+++ new/curio-1.5/tests/test_socket.py  2021-03-11 16:52:49.000000000 +0100
@@ -47,6 +47,7 @@
         results.append(('client', resp))
         results.append('client close')
         await sock.close()
+        await sleep(0.1)
 
     async def main():
         async with TaskGroup() as g:
@@ -109,6 +110,7 @@
         results.append(('client', resp))
         results.append('client close')
         await sock.close()
+        await sleep(0.1)
 
     async def main():
         async with TaskGroup() as g:
@@ -214,6 +216,7 @@
         results.append(('client', resp))
         results.append('client close')
         await sock.close()
+        await sleep(0.1)
 
     async def main():
         async with TaskGroup() as g:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.4/tests/test_task.py 
new/curio-1.5/tests/test_task.py
--- old/curio-1.4/tests/test_task.py    2020-08-24 11:41:46.000000000 +0200
+++ new/curio-1.5/tests/test_task.py    2021-03-11 16:52:49.000000000 +0100
@@ -101,18 +101,35 @@
         return x + y
 
     async def main():
-        async with TaskGroup(wait=all) as g:
+        t0 = await spawn(sleep, 5, daemon=True)
+        async with TaskGroup([t0], wait=all) as g:
             t1 = await g.spawn(child, 1, 1)
             t2 = await g.spawn(child, 2, 2, daemon=True)
             t3 = await g.spawn(child, 3, "3", daemon=True)
-            t4 = await g.spawn(sleep, 0.5, daemon=True)
+            t4 = await g.spawn(sleep, 5, daemon=True)
 
         assert t1.result == 2
+        # Verify that results from daemonic tasks are disregarded
         assert g.results == [2]
-
+        # Verify that daemon tasks get cancelled
+        assert t0.cancelled        
+        assert t4.cancelled
 
     kernel.run(main())
 
+def test_task_group_daemon_error(kernel):
+    async def main():
+        try:
+            async with TaskGroup(wait=all) as g:
+                t = await g.spawn(sleep, 5, daemon=True)
+                raise ValueError()
+        except ValueError:
+            pass
+        # Verify that the daemon task got cancelled
+        assert t.cancelled
+
+    kernel.run(main())
+    
 def test_task_group_existing(kernel):
     evt = Event()
     async def child(x, y):

Reply via email to