Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-duet for openSUSE:Factory 
checked in at 2022-09-29 18:12:07
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-duet (Old)
 and      /work/SRC/openSUSE:Factory/.python-duet.new.2275 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-duet"

Thu Sep 29 18:12:07 2022 rev:2 rq:1006513 version:0.2.7

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-duet/python-duet.changes  2022-02-21 
17:47:00.627601356 +0100
+++ /work/SRC/openSUSE:Factory/.python-duet.new.2275/python-duet.changes        
2022-09-29 18:12:18.291131900 +0200
@@ -1,0 +2,12 @@
+Tue Sep 27 19:11:09 UTC 2022 - Yogalakshmi Arunachalam <[email protected]>
+
+- Update to 0.2.7
+  * Fix timeout clipping in Scheduler.tick (#67) 
+  * Add support for overloaded sync callbacks (#66)
+  * Add support for wrapping abstract methods with duet.sync (#63)
+  * Drop support for python 3.6 (#62)
+  * Update ci.yml (#61)
+  * Ensure that duet.sleep raises an exception if outer timeout elapses first 
(#58)
+  * Update to latest version of black (#57)
+
+-------------------------------------------------------------------

Old:
----
  duet-0.2.3.tar.gz

New:
----
  duet-0.2.7.tar.gz

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

Other differences:
------------------
++++++ python-duet.spec ++++++
--- /var/tmp/diff_new_pack.Zyxm6R/_old  2022-09-29 18:12:18.907133103 +0200
+++ /var/tmp/diff_new_pack.Zyxm6R/_new  2022-09-29 18:12:18.911133111 +0200
@@ -19,7 +19,7 @@
 %{?!python_module:%define python_module() python3-%{**}}
 %define skip_python2 1
 Name:           python-duet
-Version:        0.2.3
+Version:        0.2.7
 Release:        0
 Summary:        A simple future-based async library for python
 License:        Apache-2.0
@@ -32,8 +32,8 @@
 BuildRequires:  %{python_module asyncio-contextmanager}
 BuildRequires:  %{python_module contextvars}
 %endif
-BuildRequires:  python-rpm-macros
 BuildRequires:  fdupes
+BuildRequires:  python-rpm-macros
 Requires:       python-typing_extensions
 %if %python_version_nodots <= 36
 Requires:       python-asyncio-contextmanager

++++++ duet-0.2.3.tar.gz -> duet-0.2.7.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/duet-0.2.3/PKG-INFO new/duet-0.2.7/PKG-INFO
--- old/duet-0.2.3/PKG-INFO     2021-11-02 00:29:39.942416400 +0100
+++ new/duet-0.2.7/PKG-INFO     2022-06-18 03:52:56.991586000 +0200
@@ -1,49 +1,52 @@
 Metadata-Version: 2.1
 Name: duet
-Version: 0.2.3
+Version: 0.2.7
 Summary: A simple future-based async library for python.
 Home-page: http://github.com/google/duet
 Author: The Duet Authors
 Author-email: [email protected]
 License: Apache 2
-Description: # duet
-        
-        A simple future-based async library for python
-        
-        Duet takes inspiration from the amazing 
[trio](https://trio.readthedocs.io/en/stable/)
-        library and the [structured 
concurrency](https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/)
-        approach to async programming that it uses.
-        However, duet differs from trio in two major ways:
-        
-        - Instead of a full-blown implementation of asynchronous IO, duet 
relies  on the
-          `Future` interface for parallelism, and provides a way to run 
async/await
-          coroutines around those `Future`s. This is useful if you are using 
an API that
-          returns futures, such as RPC libraries like gRPC. The standard 
`Future`
-          interface does not implement `__await__` directly, so `Future` 
instances must
-          be wrapped in `duet.AwaitableFuture`.
-        
-        - duet is re-entrant. At the top level, you run async code by calling
-          `duet.run(foo)`. Inside `foo` suppose you call a function that has 
not yet
-          been fully refactored to be asynchronous, but itself calls 
`duet.run(bar)`.
-          Most async libraries, including `trio` and `asyncio`, will raise an 
exception
-          if you try to "re-enter" the event loop in this way, but duet allows 
it. We
-          have found that this can simplify the process of refactoring code to 
be
-          asynchronous because you don't have to completely separate the sync 
and async
-          parts of your codebase all at once.
-        
-        ## Installation
-          
-        Install from pypi:
-        
-        ```
-        pip install duet
-        ```
-        
-        ## Note
-        
-        duet is not an official Google project.
-        
 Platform: UNKNOWN
-Requires-Python: >=3.6.0
+Requires-Python: >=3.7.0
 Description-Content-Type: text/markdown
 Provides-Extra: dev_env
+License-File: LICENSE
+
+# duet
+
+A simple future-based async library for python
+
+Duet takes inspiration from the amazing 
[trio](https://trio.readthedocs.io/en/stable/)
+library and the [structured 
concurrency](https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/)
+approach to async programming that it uses.
+However, duet differs from trio in two major ways:
+
+- Instead of a full-blown implementation of asynchronous IO, duet relies  on 
the
+  `Future` interface for parallelism, and provides a way to run async/await
+  coroutines around those `Future`s. This is useful if you are using an API 
that
+  returns futures, such as RPC libraries like gRPC. The standard `Future`
+  interface does not implement `__await__` directly, so `Future` instances must
+  be wrapped in `duet.AwaitableFuture`.
+
+- duet is re-entrant. At the top level, you run async code by calling
+  `duet.run(foo)`. Inside `foo` suppose you call a function that has not yet
+  been fully refactored to be asynchronous, but itself calls `duet.run(bar)`.
+  Most async libraries, including `trio` and `asyncio`, will raise an exception
+  if you try to "re-enter" the event loop in this way, but duet allows it. We
+  have found that this can simplify the process of refactoring code to be
+  asynchronous because you don't have to completely separate the sync and async
+  parts of your codebase all at once.
+
+## Installation
+  
+Install from pypi:
+
+```
+pip install duet
+```
+
+## Note
+
+duet is not an official Google project.
+
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/duet-0.2.3/dev/requirements.txt 
new/duet-0.2.7/dev/requirements.txt
--- old/duet-0.2.3/dev/requirements.txt 2021-09-17 00:41:00.000000000 +0200
+++ new/duet-0.2.7/dev/requirements.txt 2022-05-04 09:11:02.000000000 +0200
@@ -1,6 +1,6 @@
-black == 20.8b1
+black == 22.3.0
 isort == 5.7.*
-mypy == 0.782.*
+mypy == 0.931.*
 pylint == 2.10.*
-pytest == 5.4.*
+pytest == 6.2.*
 twine == 3.3.*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/duet-0.2.3/duet/_version.py 
new/duet-0.2.7/duet/_version.py
--- old/duet-0.2.3/duet/_version.py     2021-11-02 00:29:05.000000000 +0100
+++ new/duet-0.2.7/duet/_version.py     2022-06-18 03:35:26.000000000 +0200
@@ -12,4 +12,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-__version__ = "0.2.3"
+__version__ = "0.2.7"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/duet-0.2.3/duet/api.py new/duet-0.2.7/duet/api.py
--- old/duet-0.2.3/duet/api.py  2021-11-02 00:03:18.000000000 +0100
+++ new/duet-0.2.7/duet/api.py  2022-06-18 03:23:31.000000000 +0200
@@ -39,15 +39,6 @@
 T = TypeVar("T")
 U = TypeVar("U")
 
-try:
-    asynccontextmanager = contextlib.asynccontextmanager
-except AttributeError:
-    # In python 3.6 asynccontextmanager isn't available from the standard 
library, so we are using
-    # equivalent third-party implementation.
-    from aiocontext import async_contextmanager
-
-    asynccontextmanager = async_contextmanager
-
 
 def run(func: Callable[..., Awaitable[T]], *args, **kwds) -> T:
     """Run an async function to completion.
@@ -67,10 +58,12 @@
 
 def sync(f: Callable[..., Awaitable[T]]) -> Callable[..., T]:
     """Decorator that adds a sync version of async function or method."""
+    if isinstance(f, classmethod):
+        raise TypeError(f"duet.sync cannot be applied to classmethod 
{f.__func__}")
     sig = inspect.signature(f)
     first_arg = next(iter(sig.parameters), None)
 
-    if first_arg == "self" or first_arg == "cls":
+    if first_arg == "self":
         # For class or instance methods, look up the method to call on the 
given
         # class or instance. This ensures that we call the right method even it
         # has been overridden in a subclass. To illustrate, consider:
@@ -89,13 +82,15 @@
         # function by name at runtime, using getattr.
 
         @functools.wraps(f)
-        def wrapped(self_or_cls, *args, **kw):
-            method = getattr(self_or_cls, f.__name__, None)
+        def wrapped(self, *args, **kw):
+            method = getattr(self, f.__name__)
             if inspect.ismethod(method) and id(method.__func__) == wrapped_id:
-                return run(f, self_or_cls, *args, **kw)
+                return run(f, self, *args, **kw)
             return run(method, *args, **kw)
 
         wrapped_id = id(wrapped)
+        if getattr(wrapped, "__isabstractmethod__", False):
+            wrapped.__isabstractmethod__ = False  # type: ignore[attr-defined]
     else:
 
         @functools.wraps(f)
@@ -138,9 +133,7 @@
 
 
 async def pmap_async(
-    func: Callable[[T], Awaitable[U]],
-    iterable: AnyIterable[T],
-    limit: Optional[int] = None,
+    func: Callable[[T], Awaitable[U]], iterable: AnyIterable[T], limit: 
Optional[int] = None
 ) -> List[U]:
     """Apply an async function to every item in iterable.
 
@@ -160,9 +153,7 @@
 
 
 async def pstarmap_async(
-    func: Callable[..., Awaitable[U]],
-    iterable: AnyIterable[Any],
-    limit: Optional[int] = None,
+    func: Callable[..., Awaitable[U]], iterable: AnyIterable[Any], limit: 
Optional[int] = None
 ) -> List[U]:
     """Apply an async function to every tuple of args in iterable.
 
@@ -256,14 +247,15 @@
 
 async def sleep(time: float) -> None:
     """Sleeps for the given length of time in seconds."""
-    try:
-        async with timeout_scope(time):
+    async with new_scope(timeout=time) as scope:
+        try:
             await AwaitableFuture()
-    except TimeoutError:
-        pass
+        except TimeoutError as e:
+            if e is not scope._timeout_error:
+                raise
 
 
-@asynccontextmanager
[email protected]
 async def deadline_scope(deadline: float) -> AsyncIterator[None]:
     """Enter a scope that will exit when the deadline elapses.
 
@@ -274,7 +266,7 @@
         yield
 
 
-@asynccontextmanager
[email protected]
 async def timeout_scope(timeout: float) -> AsyncIterator[None]:
     """Enter a scope that will exit when the timeout elapses.
 
@@ -285,7 +277,7 @@
         yield
 
 
-@asynccontextmanager
[email protected]
 async def new_scope(
     *, deadline: Optional[float] = None, timeout: Optional[float] = None
 ) -> AsyncIterator["Scope"]:
@@ -324,10 +316,11 @@
             deadline = scheduler.time() + timeout
         else:
             deadline = min(deadline, scheduler.time() + timeout)
+    scope = Scope(main_task, scheduler, tasks)
     if deadline is not None:
-        main_task.push_deadline(deadline)
+        main_task.push_deadline(deadline, scope._timeout_error)
     try:
-        yield Scope(main_task, scheduler, tasks)
+        yield scope
         await finish_tasks()
     except (impl.Interrupt, Exception) as exc:
         # Interrupt remaining tasks.
@@ -358,6 +351,7 @@
         self._main_task = main_task
         self._scheduler = scheduler
         self._tasks = tasks
+        self._timeout_error = TimeoutError()
 
     def cancel(self) -> None:
         self._main_task.interrupt(self._main_task, CancelledError())
@@ -494,29 +488,21 @@
         self.scope.spawn(func, *args, **kwds)
 
     async def pmap_async(
-        self,
-        func: Callable[[T], Awaitable[U]],
-        iterable: AnyIterable[T],
+        self, func: Callable[[T], Awaitable[U]], iterable: AnyIterable[T]
     ) -> List[U]:
         return [x async for x in self.pmap_aiter(func, iterable)]
 
     def pmap_aiter(
-        self,
-        func: Callable[[T], Awaitable[U]],
-        iterable: AnyIterable[T],
+        self, func: Callable[[T], Awaitable[U]], iterable: AnyIterable[T]
     ) -> AsyncIterator[U]:
         return pmap_aiter(self.scope, func, self.limiter.throttle(iterable))
 
     async def pstarmap_async(
-        self,
-        func: Callable[..., Awaitable[U]],
-        iterable: AnyIterable[Any],
+        self, func: Callable[..., Awaitable[U]], iterable: AnyIterable[Any]
     ) -> List[U]:
         return [x async for x in self.pstarmap_aiter(func, iterable)]
 
     def pstarmap_aiter(
-        self,
-        func: Callable[..., Awaitable[U]],
-        iterable: AnyIterable[Any],
+        self, func: Callable[..., Awaitable[U]], iterable: AnyIterable[Any]
     ) -> AsyncIterator[U]:
         return pstarmap_aiter(self.scope, func, 
self.limiter.throttle(iterable))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/duet-0.2.3/duet/api_test.py 
new/duet-0.2.7/duet/api_test.py
--- old/duet-0.2.3/duet/api_test.py     2021-11-02 00:03:18.000000000 +0100
+++ new/duet-0.2.7/duet/api_test.py     2022-06-18 03:22:49.000000000 +0200
@@ -12,6 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import abc
 import concurrent.futures
 import inspect
 import sys
@@ -346,6 +347,15 @@
 
 
 @duet.sync
+async def test_sleep_with_timeout():
+    start = time.time()
+    with pytest.raises(TimeoutError):
+        async with duet.timeout_scope(0.5):
+            await duet.sleep(10)
+    assert abs((time.time() - start) - 0.5) < 0.2
+
+
[email protected]
 async def test_repeated_sleep():
     start = time.time()
     for _ in range(5):
@@ -353,6 +363,16 @@
     assert abs((time.time() - start) - 0.5) < 0.2
 
 
[email protected]
+async def test_repeated_sleep_with_timeout():
+    start = time.time()
+    with pytest.raises(TimeoutError):
+        async with duet.timeout_scope(0.5):
+            for _ in range(5):
+                await duet.sleep(0.2)
+    assert abs((time.time() - start) - 0.5) < 0.2
+
+
 class TestScope:
     @duet.sync
     async def test_run_all(self):
@@ -522,3 +542,45 @@
 
         scope.spawn(set_results, f0, f1)
         await f1
+
+
+class TestSync:
+    def test_sync_on_overridden_method(self):
+        class Foo:
+            async def foo_async(self, a: int) -> int:
+                return a * 2
+
+            foo = duet.sync(foo_async)
+
+        class Bar(Foo):
+            async def foo_async(self, a: int) -> int:
+                return a * 3
+
+        assert Foo().foo(5) == 10
+        assert Bar().foo(5) == 15
+
+    def test_sync_on_abstract_method(self):
+        class Foo(abc.ABC):
+            @abc.abstractmethod
+            async def foo_async(self, a: int) -> int:
+                pass
+
+            foo = duet.sync(foo_async)
+
+        class Bar(Foo):
+            async def foo_async(self, a: int) -> int:
+                return a * 3
+
+        with pytest.raises(TypeError, match="Can't instantiate abstract class 
Foo.*foo_async"):
+            _ = Foo()
+        assert Bar().foo(5) == 15
+
+    def test_sync_on_classmethod(self):
+        with pytest.raises(TypeError, match="duet.sync cannot be applied to 
classmethod"):
+
+            class _Foo:
+                @classmethod
+                async def foo_async(cls, a: int) -> int:
+                    return a * 2
+
+                foo = duet.sync(foo_async)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/duet-0.2.3/duet/futuretools.py 
new/duet-0.2.7/duet/futuretools.py
--- old/duet-0.2.3/duet/futuretools.py  2021-08-25 23:23:22.000000000 +0200
+++ new/duet-0.2.7/duet/futuretools.py  2022-05-04 09:11:02.000000000 +0200
@@ -16,7 +16,10 @@
 from concurrent.futures import Future
 from typing import Any, Callable, Generator, Generic, Optional, Tuple, Type, 
TypeVar
 
-from typing_extensions import Protocol
+try:
+    from typing import Protocol
+except ImportError:
+    from typing_extensions import Protocol  # type: ignore[misc]
 
 try:
     import grpc
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/duet-0.2.3/duet/impl.py new/duet-0.2.7/duet/impl.py
--- old/duet-0.2.3/duet/impl.py 2021-09-18 01:29:58.000000000 +0200
+++ new/duet-0.2.7/duet/impl.py 2022-06-18 03:23:31.000000000 +0200
@@ -68,10 +68,7 @@
 
 class Task(Generic[T]):
     def __init__(
-        self,
-        awaitable: Awaitable[T],
-        scheduler: "Scheduler",
-        main_task: Optional["Task"],
+        self, awaitable: Awaitable[T], scheduler: "Scheduler", main_task: 
Optional["Task"]
     ) -> None:
         self.scheduler = scheduler
         self.main_task = main_task
@@ -84,8 +81,9 @@
         self._result: Optional[T] = None
         self._error: Optional[Exception] = None
         self._deadlines: List[DeadlineEntry] = []
-        if main_task and main_task.deadline is not None:
-            self.push_deadline(main_task.deadline)
+        if main_task and main_task.deadline_entry is not None:
+            entry = main_task.deadline_entry
+            self.push_deadline(deadline=entry.deadline, 
timeout_error=entry.timeout_error)
         self._generator = awaitable.__await__()  # Returns coroutine generator.
         if isinstance(awaitable, Coroutine):
             awaitable.cr_frame.f_locals.setdefault(LOCALS_TASK_SCHEDULER, 
scheduler)
@@ -152,10 +150,14 @@
         finally:
             _current_task.reset(token)
 
-    def push_deadline(self, deadline: float) -> None:
+    def push_deadline(self, deadline: float, timeout_error: TimeoutError) -> 
None:
         if self._deadlines:
-            deadline = min(self._deadlines[-1].deadline, deadline)
-        entry = self.scheduler.add_deadline(self, deadline)
+            entry = self._deadlines[-1]
+            if entry.deadline < deadline:
+                deadline = entry.deadline
+                timeout_error = entry.timeout_error
+        entry = DeadlineEntry(self, deadline, timeout_error)
+        self.scheduler.add_deadline(entry)
         self._deadlines.append(entry)
 
     def pop_deadline(self) -> None:
@@ -163,8 +165,8 @@
         entry.valid = False
 
     @property
-    def deadline(self) -> Optional[float]:
-        return self._deadlines[-1].deadline if self._deadlines else None
+    def deadline_entry(self) -> Optional["DeadlineEntry"]:
+        return self._deadlines[-1] if self._deadlines else None
 
     def interrupt(self, task, error):
         if self.done or not self.interruptible or self._interrupt:
@@ -293,9 +295,10 @@
 
     _counter = itertools.count()
 
-    def __init__(self, task: Task, deadline: float):
+    def __init__(self, task: Task, deadline: float, timeout_error: 
TimeoutError):
         self.task = task
         self.deadline = deadline
+        self.timeout_error = timeout_error
         self.count = next(self._counter)
         self._cmp_val = (deadline, self.count)
         self.valid = True
@@ -345,10 +348,8 @@
     def time(self) -> float:
         return time.time()
 
-    def add_deadline(self, task: Task, deadline: float) -> DeadlineEntry:
-        entry = DeadlineEntry(task, deadline=deadline)
+    def add_deadline(self, entry: DeadlineEntry) -> None:
         heapq.heappush(self._deadlines, entry)
-        return entry
 
     def get_next_deadline(self) -> Optional[float]:
         while self._deadlines:
@@ -358,11 +359,11 @@
             return self._deadlines[0].deadline
         return None
 
-    def get_deadline_tasks(self, deadline: float) -> Iterator[Task]:
+    def get_deadline_entries(self, deadline: float) -> Iterator[DeadlineEntry]:
         while self._deadlines and self._deadlines[0].deadline <= deadline:
             entry = heapq.heappop(self._deadlines)
             if entry.valid:
-                yield entry.task
+                yield entry
 
     def tick(self):
         """Runs the scheduler ahead by one tick.
@@ -397,8 +398,8 @@
                 except TimeoutError:
                     pass
             if not ready_tasks:
-                for task in self.get_deadline_tasks(deadline):
-                    task.interrupt(task, TimeoutError())
+                for entry in self.get_deadline_entries(deadline):
+                    entry.task.interrupt(entry.task, entry.timeout_error)
                 ready_tasks = self._ready_tasks.get_all(None)
         for task in ready_tasks:
             try:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/duet-0.2.3/duet.egg-info/PKG-INFO 
new/duet-0.2.7/duet.egg-info/PKG-INFO
--- old/duet-0.2.3/duet.egg-info/PKG-INFO       2021-11-02 00:29:39.000000000 
+0100
+++ new/duet-0.2.7/duet.egg-info/PKG-INFO       2022-06-18 03:52:56.000000000 
+0200
@@ -1,49 +1,52 @@
 Metadata-Version: 2.1
 Name: duet
-Version: 0.2.3
+Version: 0.2.7
 Summary: A simple future-based async library for python.
 Home-page: http://github.com/google/duet
 Author: The Duet Authors
 Author-email: [email protected]
 License: Apache 2
-Description: # duet
-        
-        A simple future-based async library for python
-        
-        Duet takes inspiration from the amazing 
[trio](https://trio.readthedocs.io/en/stable/)
-        library and the [structured 
concurrency](https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/)
-        approach to async programming that it uses.
-        However, duet differs from trio in two major ways:
-        
-        - Instead of a full-blown implementation of asynchronous IO, duet 
relies  on the
-          `Future` interface for parallelism, and provides a way to run 
async/await
-          coroutines around those `Future`s. This is useful if you are using 
an API that
-          returns futures, such as RPC libraries like gRPC. The standard 
`Future`
-          interface does not implement `__await__` directly, so `Future` 
instances must
-          be wrapped in `duet.AwaitableFuture`.
-        
-        - duet is re-entrant. At the top level, you run async code by calling
-          `duet.run(foo)`. Inside `foo` suppose you call a function that has 
not yet
-          been fully refactored to be asynchronous, but itself calls 
`duet.run(bar)`.
-          Most async libraries, including `trio` and `asyncio`, will raise an 
exception
-          if you try to "re-enter" the event loop in this way, but duet allows 
it. We
-          have found that this can simplify the process of refactoring code to 
be
-          asynchronous because you don't have to completely separate the sync 
and async
-          parts of your codebase all at once.
-        
-        ## Installation
-          
-        Install from pypi:
-        
-        ```
-        pip install duet
-        ```
-        
-        ## Note
-        
-        duet is not an official Google project.
-        
 Platform: UNKNOWN
-Requires-Python: >=3.6.0
+Requires-Python: >=3.7.0
 Description-Content-Type: text/markdown
 Provides-Extra: dev_env
+License-File: LICENSE
+
+# duet
+
+A simple future-based async library for python
+
+Duet takes inspiration from the amazing 
[trio](https://trio.readthedocs.io/en/stable/)
+library and the [structured 
concurrency](https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/)
+approach to async programming that it uses.
+However, duet differs from trio in two major ways:
+
+- Instead of a full-blown implementation of asynchronous IO, duet relies  on 
the
+  `Future` interface for parallelism, and provides a way to run async/await
+  coroutines around those `Future`s. This is useful if you are using an API 
that
+  returns futures, such as RPC libraries like gRPC. The standard `Future`
+  interface does not implement `__await__` directly, so `Future` instances must
+  be wrapped in `duet.AwaitableFuture`.
+
+- duet is re-entrant. At the top level, you run async code by calling
+  `duet.run(foo)`. Inside `foo` suppose you call a function that has not yet
+  been fully refactored to be asynchronous, but itself calls `duet.run(bar)`.
+  Most async libraries, including `trio` and `asyncio`, will raise an exception
+  if you try to "re-enter" the event loop in this way, but duet allows it. We
+  have found that this can simplify the process of refactoring code to be
+  asynchronous because you don't have to completely separate the sync and async
+  parts of your codebase all at once.
+
+## Installation
+  
+Install from pypi:
+
+```
+pip install duet
+```
+
+## Note
+
+duet is not an official Google project.
+
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/duet-0.2.3/duet.egg-info/requires.txt 
new/duet-0.2.7/duet.egg-info/requires.txt
--- old/duet-0.2.3/duet.egg-info/requires.txt   2021-11-02 00:29:39.000000000 
+0100
+++ new/duet-0.2.7/duet.egg-info/requires.txt   2022-06-18 03:52:56.000000000 
+0200
@@ -1,12 +1,11 @@
 
-[:python_version <= "3.6"]
-asyncio-contextmanager==1.0.1
-contextvars==2.4
+[:python_version <= "3.7"]
+typing-extensions==3.10.0
 
 [dev_env]
-black==20.8b1
+black==22.3.0
 isort==5.7.*
-mypy==0.782.*
+mypy==0.931.*
 pylint==2.10.*
-pytest==5.4.*
+pytest==6.2.*
 twine==3.3.*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/duet-0.2.3/pyproject.toml 
new/duet-0.2.7/pyproject.toml
--- old/duet-0.2.3/pyproject.toml       2021-02-09 01:16:58.000000000 +0100
+++ new/duet-0.2.7/pyproject.toml       2022-05-04 09:11:02.000000000 +0200
@@ -1,6 +1,7 @@
 [tool.black]
 line-length = 100
 target_version = ['py37', 'py38']
+skip-magic-trailing-comma = true
 
 [tool.isort]
 profile = 'black'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/duet-0.2.3/requirements.txt 
new/duet-0.2.7/requirements.txt
--- old/duet-0.2.3/requirements.txt     2021-08-25 23:23:22.000000000 +0200
+++ new/duet-0.2.7/requirements.txt     2022-06-18 03:22:49.000000000 +0200
@@ -1,2 +1 @@
-asyncio-contextmanager == 1.0.1; python_version <= '3.6'
-contextvars == 2.4; python_version <= '3.6'
\ No newline at end of file
+typing-extensions == 3.10.0; python_version <= '3.7'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/duet-0.2.3/setup.py new/duet-0.2.7/setup.py
--- old/duet-0.2.3/setup.py     2021-08-25 23:23:22.000000000 +0200
+++ new/duet-0.2.7/setup.py     2022-06-18 03:22:49.000000000 +0200
@@ -56,7 +56,7 @@
     url="http://github.com/google/duet";,
     author="The Duet Authors",
     author_email="[email protected]",
-    python_requires=">=3.6.0",
+    python_requires=">=3.7.0",
     install_requires=requirements,
     extras_require={
         "dev_env": dev_requirements,

Reply via email to