Hello community,

here is the log from the commit of package python-curio for openSUSE:Factory 
checked in at 2020-06-15 20:33:11
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-curio (Old)
 and      /work/SRC/openSUSE:Factory/.python-curio.new.3606 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-curio"

Mon Jun 15 20:33:11 2020 rev:5 rq:814701 version:1.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-curio/python-curio.changes        
2020-03-05 23:24:33.201385854 +0100
+++ /work/SRC/openSUSE:Factory/.python-curio.new.3606/python-curio.changes      
2020-06-15 20:33:20.506976803 +0200
@@ -1,0 +2,10 @@
+Mon Jun 15 11:58:37 UTC 2020 - Ondřej Súkup <mimi...@gmail.com>
+
+- Update to 1.2 
+ * Removed hard dependency on contextvars
+ * Added a default selector timeout of 1 second for Windows.
+ * First crack at a Curio repl.
+ * Added a pytest plugin
+ * Slight refinement to TaskGroup result reporting.
+
+-------------------------------------------------------------------

Old:
----
  curio-1.1.tar.gz

New:
----
  curio-1.2.tar.gz

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

Other differences:
------------------
++++++ python-curio.spec ++++++
--- /var/tmp/diff_new_pack.UcRCVn/_old  2020-06-15 20:33:22.038982303 +0200
+++ /var/tmp/diff_new_pack.UcRCVn/_new  2020-06-15 20:33:22.042982317 +0200
@@ -19,7 +19,7 @@
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %define skip_python2 1
 Name:           python-curio
-Version:        1.1
+Version:        1.2
 Release:        0
 Summary:        Concurrent I/O library for Python 3
 License:        BSD-Source-Code

++++++ curio-1.1.tar.gz -> curio-1.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.1/.gitignore new/curio-1.2/.gitignore
--- old/curio-1.1/.gitignore    2020-03-01 13:27:43.000000000 +0100
+++ new/curio-1.2/.gitignore    2020-04-07 03:43:23.000000000 +0200
@@ -2,6 +2,7 @@
 __pycache__/
 .vscode
 venv*
+*.egg-info
 
 benchmarks/curio/
 benchmarks/env/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.1/CHANGES new/curio-1.2/CHANGES
--- old/curio-1.1/CHANGES       2020-03-01 13:27:43.000000000 +0100
+++ new/curio-1.2/CHANGES       2020-04-07 03:43:23.000000000 +0200
@@ -1,7 +1,43 @@
 CHANGES
 -------
-Version 1.1 - March 1, 2020
+Version 1.2 - In Progress
+
+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().
+          
+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:
+
+              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
+           normal.  Note: This feature requires Python 3.8 or newer. 
 
+03/24/2020 Added a pytest plugin for Curio. Contributed by Keith Dart.
+           See curio/pytest_plugin.py.
+
+03/02/2020 Slight refinement to TaskGroup result reporting. If no tasks
+           are spawned in a TaskGroup g, then g.exception will return None
+           to indicate no errors.  g.result will raise an exception
+           indicating that no successful result was obtained. Addresses
+           issue #314.
+
+Version 1.1 - March 1, 2020
+---------------------------
 02/24/2020 Fixed a very subtle edge case involving epoll() and duplicated
            file descriptors on Linux. The fix required a new trap
            _io_release() to explitly unregister a file descriptor
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.1/curio/__init__.py 
new/curio-1.2/curio/__init__.py
--- old/curio-1.1/curio/__init__.py     2020-03-01 13:27:43.000000000 +0100
+++ new/curio-1.2/curio/__init__.py     2020-04-07 03:43:23.000000000 +0200
@@ -1,6 +1,6 @@
 # curio/__init__.py
 
-__version__ = '1.1'
+__version__ = '1.2'
 
 from .errors import *
 from .queue import *
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.1/curio/__main__.py 
new/curio-1.2/curio/__main__.py
--- old/curio-1.1/curio/__main__.py     1970-01-01 01:00:00.000000000 +0100
+++ new/curio-1.2/curio/__main__.py     2020-04-07 03:43:23.000000000 +0200
@@ -0,0 +1,113 @@
+import ast
+import curio
+import code
+import inspect
+import sys
+import types
+import warnings
+import threading
+import signal
+import os
+
+class CurioIOInteractiveConsole(code.InteractiveConsole):
+
+    def __init__(self, locals):
+        super().__init__(locals)
+        self.compile.compiler.flags |= ast.PyCF_ALLOW_TOP_LEVEL_AWAIT
+        self.requests = curio.UniversalQueue()
+        self.response = curio.UniversalQueue()
+
+    def runcode(self, code):
+        # This coroutine is handed from the thread running the REPL to the
+        # task runner in the main thread.
+        async def run_it():
+            func = types.FunctionType(code, self.locals)
+            try:
+                # We restore the default REPL signal handler for running 
normal code
+                hand = signal.signal(signal.SIGINT, signal.default_int_handler)
+                try:
+                    coro = func()
+                finally:
+                    signal.signal(signal.SIGINT, hand)
+            except BaseException as ex:
+                await self.response.put((None, ex))
+                return
+            if not inspect.iscoroutine(coro):
+                await self.response.put((coro, None))
+                return
+
+            # For a coroutine... We're going to try and do some magic to 
intercept
+            # Control-C in an Event/Task.
+            async def watch_ctrl_c(evt, repl_task):
+                await evt.wait()
+                await repl_task.cancel()
+            evt = curio.UniversalEvent()
+            try:
+                hand = signal.signal(signal.SIGINT, lambda signo, frame: 
evt.set())
+                repl_task = await curio.spawn(coro)
+                watch_task = await curio.spawn(watch_ctrl_c, evt, repl_task)
+                try:
+                    result = await repl_task.join()
+                    response = (result, None)
+                except SystemExit:
+                    raise
+                except BaseException as e:
+                    await repl_task.wait()
+                    response = (None, e.__cause__)
+                await watch_task.cancel()
+            finally:
+                signal.signal(signal.SIGINT, hand)
+            await self.response.put(response)
+
+        self.requests.put(run_it())
+        # Get the result here...
+        result, exc = self.response.get()
+        if exc is not None:
+            try:
+                raise exc
+            except BaseException:
+                self.showtraceback()
+        else:
+            return result
+
+    # Task that runs in the main thread, executing input fed to it from above
+    async def runmain(self):
+        try:
+            hand = signal.signal(signal.SIGINT, signal.SIG_IGN)
+            while True:
+                coro = await self.requests.get()
+                if coro is None:
+                    break
+                await coro
+        finally:
+            signal.signal(signal.SIGINT, hand)
+
+def run_repl(console):
+    try:
+        banner = (
+            f'curio REPL {sys.version} on {sys.platform}\n'
+            f'Use "await" directly instead of "curio.run()".\n'
+            f'Type "help", "copyright", "credits" or "license" '
+            f'for more information.\n'
+            f'{getattr(sys, "ps1", ">>> ")}import curio'
+            )
+        console.interact(
+            banner=banner,
+            exitmsg='exiting curio REPL...')
+    finally:
+        warnings.filterwarnings(
+            'ignore',
+            message=r'^coroutine .* was never awaited$',
+            category=RuntimeWarning)
+        console.requests.put(None)
+    
+if __name__ == '__main__':
+    repl_locals = { 'curio': curio }
+    for key in {'__name__', '__package__',
+                '__loader__', '__spec__',
+                '__builtins__', '__file__'}:
+        repl_locals[key] = locals()[key]
+
+    console = CurioIOInteractiveConsole(repl_locals)
+    threading.Thread(target=run_repl, args=[console], daemon=True).start()
+    curio.run(console.runmain)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.1/curio/kernel.py 
new/curio-1.2/curio/kernel.py
--- old/curio-1.1/curio/kernel.py       2020-03-01 13:27:43.000000000 +0100
+++ new/curio-1.2/curio/kernel.py       2020-04-07 03:43:23.000000000 +0200
@@ -81,7 +81,8 @@
     Use the kernel run() method to submit work to the kernel.
     '''
 
-    def __init__(self, *, selector=None, debug=None, activations=None, 
taskcls=Task):
+    def __init__(self, *, selector=None, debug=None, activations=None, 
taskcls=Task,
+                 max_select_timeout=None if os.name != 'nt' else 1.0):
 
         # Functions to call at shutdown
         self._shutdown_funcs = []
@@ -107,6 +108,8 @@
         # Task creation class
         self._taskcls = taskcls
 
+        self._max_select_timeout = max_select_timeout
+
 
     def __del__(self):
         if self._shutdown_funcs is not None:
@@ -202,6 +205,7 @@
         selector_modify = selector.modify
         selector_select = selector.select
         selector_getkey = selector.get_key
+        selector_max_timeout = kernel._max_select_timeout
 
         ready_popleft = ready.popleft
         ready_append = ready.append
@@ -632,6 +636,8 @@
                 else:
                     current_time = time_monotonic()
                     timeout = sleepq.next_deadline(current_time)
+                    if selector_max_timeout and (timeout is None or timeout > 
selector_max_timeout):
+                        timeout = selector_max_timeout
                 try:
                     events = selector_select(timeout)
                 except OSError as e:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.1/curio/pytest_plugin.py 
new/curio-1.2/curio/pytest_plugin.py
--- old/curio-1.1/curio/pytest_plugin.py        1970-01-01 01:00:00.000000000 
+0100
+++ new/curio-1.2/curio/pytest_plugin.py        2020-04-07 03:43:23.000000000 
+0200
@@ -0,0 +1,91 @@
+# python3.7
+
+"""Plugin module for pytest.
+
+This enables easier unit tests for applications that use both Curio and 
Pytest. If you have Curio
+installed, you have the plugin and can write unit tests per the example below.
+
+Provides a fixture named `kernel`, and a marker (pytest.mark.curio) that will 
run a bare coroutine
+in a new Kernel instance.
+
+Example:
+
+    from curio import sleep
+    import pytest
+
+    # Use marker
+
+    @pytest.mark.curio
+    async def test_coro():
+        await sleep(1)
+
+
+    # Use kernel fixture
+
+    def test_app(kernel):
+
+        async def my_aapp():
+            await sleep(1)
+
+        kernel.run(my_aapp)
+"""
+
+import inspect
+import functools
+
+import pytest
+
+from curio import Kernel
+from curio import meta
+from curio import monitor
+from curio.debug import longblock, logcrash
+
+
+def _is_coroutine(obj):
+    """Check to see if an object is really a coroutine."""
+    return meta.iscoroutinefunction(obj) or inspect.isgeneratorfunction(obj)
+
+
+def pytest_configure(config):
+    """Inject documentation."""
+    config.addinivalue_line("markers",
+                            "curio: "
+                            "mark the test as a coroutine, it will be run 
using a Curio kernel.")
+
+
+@pytest.mark.tryfirst
+def pytest_pycollect_makeitem(collector, name, obj):
+    """A pytest hook to collect coroutines in a test module."""
+    if collector.funcnamefilter(name) and _is_coroutine(obj):
+        item = pytest.Function(name, parent=collector)
+        if 'curio' in item.keywords:
+            return list(collector._genfunctions(name, obj))
+
+
+@pytest.hookimpl(tryfirst=True, hookwrapper=True)
+def pytest_pyfunc_call(pyfuncitem):
+    """Run curio marked test functions in a Curio kernel instead of a normal 
function call.
+    """
+    if 'curio' in pyfuncitem.keywords:
+        pyfuncitem.obj = wrap_in_sync(pyfuncitem.obj)
+    yield
+
+
+def wrap_in_sync(func):
+    """Return a sync wrapper around an async function executing it in a 
Kernel."""
+    @functools.wraps(func)
+    def inner(**kwargs):
+        coro = func(**kwargs)
+        Kernel().run(coro, shutdown=True)
+    return inner
+
+
+# Fixture for explicitly running in Kernel instance.
+@pytest.fixture(scope='session')
+def kernel(request):
+    """Provide a Curio Kernel object for running co-routines."""
+    k = Kernel(debug=[longblock, logcrash])
+    m = monitor.Monitor(k)
+    request.addfinalizer(lambda: k.run(shutdown=True))
+    request.addfinalizer(m.close)
+    return k
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.1/curio/task.py new/curio-1.2/curio/task.py
--- old/curio-1.1/curio/task.py 2020-03-01 13:27:43.000000000 +0100
+++ new/curio-1.2/curio/task.py 2020-04-07 03:43:23.000000000 +0200
@@ -14,7 +14,6 @@
 import linecache
 import traceback
 import os.path
-import contextvars
 
 log = logging.getLogger(__name__)
 
@@ -142,9 +141,6 @@
         # Timeout deadline stack
         self._deadlines = []
 
-        # Contextvars support
-        self._context = contextvars.copy_context()
-
     def __repr__(self):
         return f'{type(self).__name__}(id={self.id}, name={self.name!r}, 
state={self.state!r})'
 
@@ -265,6 +261,7 @@
     taskcls keyword argument to the Curio kernel.
     '''
     def __init__(self, coro):
+        import contextvars
         super().__init__(coro)
         self._context = contextvars.copy_context()
 
@@ -391,6 +388,8 @@
     def result(self):
         if not self._joined:
             raise RuntimeError("Task group not yet terminated")
+        if not self.completed:
+            raise RuntimeError("No task successfully completed")
         return self.completed.result
 
     # Property that returns the exception of the first completed task
@@ -398,7 +397,7 @@
     def exception(self):
         if not self._joined:
             raise RuntimeError("Task group not yet terminated")
-        return self.completed.exception
+        return self.completed.exception if self.completed else None
 
     # Property that returns all task results (in task creation order)
     @property
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.1/docs/conf.py new/curio-1.2/docs/conf.py
--- old/curio-1.1/docs/conf.py  2020-03-01 13:27:43.000000000 +0100
+++ new/curio-1.2/docs/conf.py  2020-04-07 03:43:23.000000000 +0200
@@ -60,9 +60,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = '1.1'
+version = '1.2'
 # The full version, including alpha/beta/rc tags.
-release = '1.1'
+release = '1.2'
 
 # 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.1/docs/tutorial.rst 
new/curio-1.2/docs/tutorial.rst
--- old/curio-1.1/docs/tutorial.rst     2020-03-01 13:27:43.000000000 +0100
+++ new/curio-1.2/docs/tutorial.rst     2020-04-07 03:43:23.000000000 +0200
@@ -361,7 +361,8 @@
 
     # Dispatch task that forwards incoming messages to subscribers
     async def dispatcher():
-        async for msg in messages:
+        while True:
+            msg = await messages.get()
             for q in list(subscribers):
                 await q.put(msg)
 
@@ -374,7 +375,8 @@
         queue = Queue()
         subscribers.add(queue)
         try:
-            async for msg in queue:
+            while True:
+                msg = await queue.get()
                 print(name, 'got', msg)
         finally:
             subscribers.discard(queue)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.1/setup.cfg new/curio-1.2/setup.cfg
--- old/curio-1.1/setup.cfg     2020-03-01 13:27:43.000000000 +0100
+++ new/curio-1.2/setup.cfg     2020-04-07 03:43:23.000000000 +0200
@@ -8,3 +8,5 @@
 testpaths = tests
 addopts = --verbose
           --ignore=setup.py --ignore=docs/conf.py
+markers =
+    internet: mark tests as requiring internet connectivity (deselect with '-m 
"not internet"')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.1/setup.py new/curio-1.2/setup.py
--- old/curio-1.1/setup.py      2020-03-01 13:27:43.000000000 +0100
+++ new/curio-1.2/setup.py      2020-04-07 03:43:23.000000000 +0200
@@ -14,7 +14,7 @@
       description="Curio",
       long_description=long_description,
       license="BSD",
-      version="1.1",
+      version="1.2",
       author="David Beazley",
       author_email="d...@dabeaz.com",
       maintainer="David Beazley",
@@ -25,6 +25,9 @@
       extras_require={
           'test': tests_require,
       },
+      python_requires='>= 3.6',
+      entry_points={"pytest11": ["curio = curio.pytest_plugin"]},
       classifiers=[
           'Programming Language :: Python :: 3',
+          "Framework :: Pytest",
       ])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.1/tests/test_network.py 
new/curio-1.2/tests/test_network.py
--- old/curio-1.1/tests/test_network.py 2020-03-01 13:27:43.000000000 +0100
+++ new/curio-1.2/tests/test_network.py 2020-04-07 03:43:23.000000000 +0200
@@ -250,6 +250,7 @@
 
     kernel.run(main())
 
+@pytest.mark.internet
 def test_ssl_outgoing(kernel):
     async def main():
         c = await network.open_connection('google.com', 443, ssl=True, 
server_hostname='google.com')
@@ -312,6 +313,7 @@
 
     kernel.run(main())
 
+@pytest.mark.internet
 def test_errors(kernel):
     async def main():
         with pytest.raises(ValueError):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.1/tests/test_sync.py 
new/curio-1.2/tests/test_sync.py
--- old/curio-1.1/tests/test_sync.py    2020-03-01 13:27:43.000000000 +0100
+++ new/curio-1.2/tests/test_sync.py    2020-04-07 03:43:23.000000000 +0200
@@ -788,6 +788,7 @@
         async def main():
             evt = UniversalEvent()
             t1 = await spawn(event_waiter, evt)
+            await sleep(0.05)
             t2 = threading.Thread(target=asyncio.run, args=[event_setter(evt, 
1)])
             t2.start()
             await t1.join()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curio-1.1/tests/test_task.py 
new/curio-1.2/tests/test_task.py
--- old/curio-1.1/tests/test_task.py    2020-03-01 13:27:43.000000000 +0100
+++ new/curio-1.2/tests/test_task.py    2020-04-07 03:43:23.000000000 +0200
@@ -439,6 +439,19 @@
 
     kernel.run(main())
 
+def test_task_group_empty(kernel):
+    async def main():
+        async with TaskGroup() as g:
+            pass
+
+        assert g.exception is None
+        assert g.exceptions == []
+        assert g.results == []
+        with pytest.raises(RuntimeError):
+             g.result
+
+    kernel.run(main())
+
 def test_defer_cancellation(kernel):
     async def cancel_me(e1, e2):
         with pytest.raises(CancelledError):
@@ -556,10 +569,9 @@
         await c.send(i)
     await c.send(None)   # Sentinel
 
-import contextvars
-x = contextvars.ContextVar('x', default=0)
-
 def test_contextvars():
+    import contextvars
+    x = contextvars.ContextVar('x', default=0)
     from curio.task import ContextTask
     events = []
     async def countdown(n):


Reply via email to