summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCoprDistGit <infra@openeuler.org>2023-05-15 07:53:42 +0000
committerCoprDistGit <infra@openeuler.org>2023-05-15 07:53:42 +0000
commit98e17ad559e7c56e45de0e8ffff14d46f82a83d5 (patch)
tree09c4da1187d88446699ef582c2cb9ff5378675e5
parentfcc40157edf997fa36fb2fb59d526b20dcef3c95 (diff)
automatic import of python-aiofastforward
-rw-r--r--.gitignore1
-rw-r--r--python-aiofastforward.spec582
-rw-r--r--sources1
3 files changed, 584 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index e69de29..0b5e13d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1 @@
+/aiofastforward-0.0.24.tar.gz
diff --git a/python-aiofastforward.spec b/python-aiofastforward.spec
new file mode 100644
index 0000000..20b84a3
--- /dev/null
+++ b/python-aiofastforward.spec
@@ -0,0 +1,582 @@
+%global _empty_manifest_terminate_build 0
+Name: python-aiofastforward
+Version: 0.0.24
+Release: 1
+Summary: Fast-forward time in asyncio Python by patching loop.time, loop.call_later, loop.call_at, and asyncio.sleep
+License: MIT License
+URL: https://github.com/michalc/aiofastforward
+Source0: https://mirrors.nju.edu.cn/pypi/web/packages/70/4e/d54620e864661067145bfe34c7abb008cd2a171036c3121b8c625def59a4/aiofastforward-0.0.24.tar.gz
+BuildArch: noarch
+
+
+%description
+# aiofastforward [![CircleCI](https://circleci.com/gh/michalc/aiofastforward.svg?style=svg)](https://circleci.com/gh/michalc/aiofastforward) [![Maintainability](https://api.codeclimate.com/v1/badges/45d56d9e0d1d408f0fd8/maintainability)](https://codeclimate.com/github/michalc/aiofastforward/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/45d56d9e0d1d408f0fd8/test_coverage)](https://codeclimate.com/github/michalc/aiofastforward/test_coverage)
+
+Fast-forward time in asyncio Python by patching [loop.call_later](https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.call_later), [loop.call_at](https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.call_at), [loop.time](https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.time), and [asyncio.sleep](https://docs.python.org/3/library/asyncio-task.html#asyncio.sleep). This allows you to test asynchronous code synchronously.
+
+Inspired by [AngularJS $timeout.$flush](https://docs.angularjs.org/api/ngMock/service/$timeout#flush).
+
+
+## Installation
+
+```bash
+pip install aiofastforward
+```
+
+
+## Usage
+
+Patching is done through a context manager, similar to [unittest.patch](https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch).
+
+```python
+import asyncio
+from aiofastforward import FastForward
+
+loop = asyncio.get_event_loop()
+with FastForward(loop) as forward:
+ # Call production function(s), that call asyncio.sleep, loop.call_later,
+ # loop.call_at, or loop.time
+ # ...
+
+ # Fast-forward time 1 second
+ # asyncio.sleeps, and loop.call_at and loop.call_later callbacks
+ # will be called: as though 1 second of real-world time has passed
+ await forward(1)
+
+ # More production functions or assertions
+ # ...
+```
+
+## Examples
+
+### asyncio.sleep
+
+```python
+# Production code
+async def sleeper(callback):
+ await asyncio.sleep(1)
+ callback(0)
+ await asyncio.sleep(2)
+
+# Test code
+from unittest.mock import Mock, call
+loop = asyncio.get_event_loop()
+callback = Mock()
+
+with aiofastforward.FastForward(loop) as forward:
+ asyncio.ensure_future(sleeper())
+
+ await forward(1) # Move time forward one second
+ self.assertEqual(callback.mock_calls, [])
+ await forward(1) # Move time forward another second
+ self.assertEqual(callback.mock_calls, [call(0)])
+```
+
+### loop.call_later
+
+```python
+# Production code
+async def schedule_callback(loop, callback):
+ loop.call_later(1, callback, 0)
+ loop.call_later(2, callback, 1)
+
+# Test code
+from unittest.mock import Mock, call
+loop = asyncio.get_event_loop()
+
+with aiofastforward.FastForward(loop) as forward:
+ callback = Mock()
+ await schedule_callback(loop, callback)
+
+ await forward(1) # Move time forward one second
+ self.assertEqual(callback.mock_calls, [call(0)])
+ await forward(1) # Move time forward another second
+ self.assertEqual(callback.mock_calls, [call(0), call(1)])
+```
+
+### loop.call_at
+
+```python
+# Production code
+async def schedule_callback(loop, callback):
+ now = loop.time()
+ loop.call_at(now + 1, callback, 0)
+ loop.call_at(now + 2, callback, 1)
+
+# Test code
+from unittest.mock import Mock, call
+loop = asyncio.get_event_loop()
+
+with aiofastforward.FastForward(loop) as forward:
+ callback = Mock()
+ await schedule_callback(loop, callback)
+
+ await forward(1) # Move time forward one second
+ self.assertEqual(callback.mock_calls, [call(0)])
+ await forward(1) # Move time forward another second
+ self.assertEqual(callback.mock_calls, [call(0), call(1)])
+```
+
+
+## `forward`ing time can block
+
+`await forward(a)` only moves time forward, i.e. resolve calls to `asyncio.sleep` or calls the callbacks of `call_at` or `call_later`, once there are sufficient such calls that time could have progressed that amount. Calls to IO functions, even if they take non-zero amounts of real time in the test, do not advance the patched "pseudo-timeline": they are treated as instantanous.
+
+This means that there are cases where `await forward(a)` will block forever.
+
+
+```python
+# Production code
+async def sleeper():
+ await asyncio.sleep(1)
+
+# Test code
+loop = asyncio.get_event_loop()
+
+with aiofastforward.FastForward(loop) as forward:
+ asyncio.ensure_future(sleeper())
+
+ await forward(2) # Will block forever
+```
+
+To avoid this, ensure you only `await forward` an amount less than or equal to how much pseudo-time that will be progressed by `asyncio.sleep`, `call_at` or `call_later`.
+
+```python
+# Production code
+async def sleeper(callback):
+ await asyncio.sleep(1)
+ callback(0)
+ await asyncio.sleep(1)
+ callback(1)
+
+# Test code
+from unittest.mock import Mock, call
+loop = asyncio.get_event_loop()
+
+with aiofastforward.FastForward(loop) as forward:
+ asyncio.ensure_future(sleeper(callback))
+ start_time = loop.time()
+
+ await forward(1.5) # The second sleep will have been called, but not resolved
+ self.assertEqual(loop.time(), start_time + 1.5)
+ self.assertEqual(callback.mock_calls, [call(0)])
+```
+
+The justification for this design are the consequences of the the alternative: if it _wouldn't_ block. This would mean that all sleeps and callbacks would have to be registered _before_ the call to `forward`, and this in turn would lead to less flexible test code.
+
+For example, the production code may have a chain of 10 `asyncio.sleep(1)`, and in the test you would like to `await forward(10)` to assert on the state of the system after these. At the time of calling `await forward(10)` however, at most one of the `asyncio.sleep(1)` would have been called. Not blocking would mean that after `await forward(10)`, the pseudo-timeline in the world of the patched production code would not have moved forward ten seconds.
+
+
+## Differences between aiofastforward.FastForward and [asynctest.ClockedTestCase](https://asynctest.readthedocs.io/en/latest/asynctest.case.html#asynctest.ClockedTestCase)
+
+There is overlap in functionality: both support fast-forwarding time in terms of loop.call_later and loop.call_at. However, there are properties that FastForward has that ClockedTestCase does not:
+
+- FastForward is not coupled to any particular test framework. The only requirement is that the test code must be in an async function. If you wish, you can use FastForward in an [asynctest.TestCase](https://asynctest.readthedocs.io/en/latest/asynctest.case.html#asynctest.TestCase) test.
+- FastForward supports fast-forwarding asyncio.sleep.
+- FastForward allows fast-forwarding time in any event loop, not just the one the test code runs in.
+
+ClockedTestCase does have an advantage over FastForward, which may be important for some uses:
+
+- ClockedTestCase supports Python 3.4 onwards, while FastForward supports Python 3.5.0 onwards.
+
+
+
+
+%package -n python3-aiofastforward
+Summary: Fast-forward time in asyncio Python by patching loop.time, loop.call_later, loop.call_at, and asyncio.sleep
+Provides: python-aiofastforward
+BuildRequires: python3-devel
+BuildRequires: python3-setuptools
+BuildRequires: python3-pip
+%description -n python3-aiofastforward
+# aiofastforward [![CircleCI](https://circleci.com/gh/michalc/aiofastforward.svg?style=svg)](https://circleci.com/gh/michalc/aiofastforward) [![Maintainability](https://api.codeclimate.com/v1/badges/45d56d9e0d1d408f0fd8/maintainability)](https://codeclimate.com/github/michalc/aiofastforward/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/45d56d9e0d1d408f0fd8/test_coverage)](https://codeclimate.com/github/michalc/aiofastforward/test_coverage)
+
+Fast-forward time in asyncio Python by patching [loop.call_later](https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.call_later), [loop.call_at](https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.call_at), [loop.time](https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.time), and [asyncio.sleep](https://docs.python.org/3/library/asyncio-task.html#asyncio.sleep). This allows you to test asynchronous code synchronously.
+
+Inspired by [AngularJS $timeout.$flush](https://docs.angularjs.org/api/ngMock/service/$timeout#flush).
+
+
+## Installation
+
+```bash
+pip install aiofastforward
+```
+
+
+## Usage
+
+Patching is done through a context manager, similar to [unittest.patch](https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch).
+
+```python
+import asyncio
+from aiofastforward import FastForward
+
+loop = asyncio.get_event_loop()
+with FastForward(loop) as forward:
+ # Call production function(s), that call asyncio.sleep, loop.call_later,
+ # loop.call_at, or loop.time
+ # ...
+
+ # Fast-forward time 1 second
+ # asyncio.sleeps, and loop.call_at and loop.call_later callbacks
+ # will be called: as though 1 second of real-world time has passed
+ await forward(1)
+
+ # More production functions or assertions
+ # ...
+```
+
+## Examples
+
+### asyncio.sleep
+
+```python
+# Production code
+async def sleeper(callback):
+ await asyncio.sleep(1)
+ callback(0)
+ await asyncio.sleep(2)
+
+# Test code
+from unittest.mock import Mock, call
+loop = asyncio.get_event_loop()
+callback = Mock()
+
+with aiofastforward.FastForward(loop) as forward:
+ asyncio.ensure_future(sleeper())
+
+ await forward(1) # Move time forward one second
+ self.assertEqual(callback.mock_calls, [])
+ await forward(1) # Move time forward another second
+ self.assertEqual(callback.mock_calls, [call(0)])
+```
+
+### loop.call_later
+
+```python
+# Production code
+async def schedule_callback(loop, callback):
+ loop.call_later(1, callback, 0)
+ loop.call_later(2, callback, 1)
+
+# Test code
+from unittest.mock import Mock, call
+loop = asyncio.get_event_loop()
+
+with aiofastforward.FastForward(loop) as forward:
+ callback = Mock()
+ await schedule_callback(loop, callback)
+
+ await forward(1) # Move time forward one second
+ self.assertEqual(callback.mock_calls, [call(0)])
+ await forward(1) # Move time forward another second
+ self.assertEqual(callback.mock_calls, [call(0), call(1)])
+```
+
+### loop.call_at
+
+```python
+# Production code
+async def schedule_callback(loop, callback):
+ now = loop.time()
+ loop.call_at(now + 1, callback, 0)
+ loop.call_at(now + 2, callback, 1)
+
+# Test code
+from unittest.mock import Mock, call
+loop = asyncio.get_event_loop()
+
+with aiofastforward.FastForward(loop) as forward:
+ callback = Mock()
+ await schedule_callback(loop, callback)
+
+ await forward(1) # Move time forward one second
+ self.assertEqual(callback.mock_calls, [call(0)])
+ await forward(1) # Move time forward another second
+ self.assertEqual(callback.mock_calls, [call(0), call(1)])
+```
+
+
+## `forward`ing time can block
+
+`await forward(a)` only moves time forward, i.e. resolve calls to `asyncio.sleep` or calls the callbacks of `call_at` or `call_later`, once there are sufficient such calls that time could have progressed that amount. Calls to IO functions, even if they take non-zero amounts of real time in the test, do not advance the patched "pseudo-timeline": they are treated as instantanous.
+
+This means that there are cases where `await forward(a)` will block forever.
+
+
+```python
+# Production code
+async def sleeper():
+ await asyncio.sleep(1)
+
+# Test code
+loop = asyncio.get_event_loop()
+
+with aiofastforward.FastForward(loop) as forward:
+ asyncio.ensure_future(sleeper())
+
+ await forward(2) # Will block forever
+```
+
+To avoid this, ensure you only `await forward` an amount less than or equal to how much pseudo-time that will be progressed by `asyncio.sleep`, `call_at` or `call_later`.
+
+```python
+# Production code
+async def sleeper(callback):
+ await asyncio.sleep(1)
+ callback(0)
+ await asyncio.sleep(1)
+ callback(1)
+
+# Test code
+from unittest.mock import Mock, call
+loop = asyncio.get_event_loop()
+
+with aiofastforward.FastForward(loop) as forward:
+ asyncio.ensure_future(sleeper(callback))
+ start_time = loop.time()
+
+ await forward(1.5) # The second sleep will have been called, but not resolved
+ self.assertEqual(loop.time(), start_time + 1.5)
+ self.assertEqual(callback.mock_calls, [call(0)])
+```
+
+The justification for this design are the consequences of the the alternative: if it _wouldn't_ block. This would mean that all sleeps and callbacks would have to be registered _before_ the call to `forward`, and this in turn would lead to less flexible test code.
+
+For example, the production code may have a chain of 10 `asyncio.sleep(1)`, and in the test you would like to `await forward(10)` to assert on the state of the system after these. At the time of calling `await forward(10)` however, at most one of the `asyncio.sleep(1)` would have been called. Not blocking would mean that after `await forward(10)`, the pseudo-timeline in the world of the patched production code would not have moved forward ten seconds.
+
+
+## Differences between aiofastforward.FastForward and [asynctest.ClockedTestCase](https://asynctest.readthedocs.io/en/latest/asynctest.case.html#asynctest.ClockedTestCase)
+
+There is overlap in functionality: both support fast-forwarding time in terms of loop.call_later and loop.call_at. However, there are properties that FastForward has that ClockedTestCase does not:
+
+- FastForward is not coupled to any particular test framework. The only requirement is that the test code must be in an async function. If you wish, you can use FastForward in an [asynctest.TestCase](https://asynctest.readthedocs.io/en/latest/asynctest.case.html#asynctest.TestCase) test.
+- FastForward supports fast-forwarding asyncio.sleep.
+- FastForward allows fast-forwarding time in any event loop, not just the one the test code runs in.
+
+ClockedTestCase does have an advantage over FastForward, which may be important for some uses:
+
+- ClockedTestCase supports Python 3.4 onwards, while FastForward supports Python 3.5.0 onwards.
+
+
+
+
+%package help
+Summary: Development documents and examples for aiofastforward
+Provides: python3-aiofastforward-doc
+%description help
+# aiofastforward [![CircleCI](https://circleci.com/gh/michalc/aiofastforward.svg?style=svg)](https://circleci.com/gh/michalc/aiofastforward) [![Maintainability](https://api.codeclimate.com/v1/badges/45d56d9e0d1d408f0fd8/maintainability)](https://codeclimate.com/github/michalc/aiofastforward/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/45d56d9e0d1d408f0fd8/test_coverage)](https://codeclimate.com/github/michalc/aiofastforward/test_coverage)
+
+Fast-forward time in asyncio Python by patching [loop.call_later](https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.call_later), [loop.call_at](https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.call_at), [loop.time](https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.time), and [asyncio.sleep](https://docs.python.org/3/library/asyncio-task.html#asyncio.sleep). This allows you to test asynchronous code synchronously.
+
+Inspired by [AngularJS $timeout.$flush](https://docs.angularjs.org/api/ngMock/service/$timeout#flush).
+
+
+## Installation
+
+```bash
+pip install aiofastforward
+```
+
+
+## Usage
+
+Patching is done through a context manager, similar to [unittest.patch](https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch).
+
+```python
+import asyncio
+from aiofastforward import FastForward
+
+loop = asyncio.get_event_loop()
+with FastForward(loop) as forward:
+ # Call production function(s), that call asyncio.sleep, loop.call_later,
+ # loop.call_at, or loop.time
+ # ...
+
+ # Fast-forward time 1 second
+ # asyncio.sleeps, and loop.call_at and loop.call_later callbacks
+ # will be called: as though 1 second of real-world time has passed
+ await forward(1)
+
+ # More production functions or assertions
+ # ...
+```
+
+## Examples
+
+### asyncio.sleep
+
+```python
+# Production code
+async def sleeper(callback):
+ await asyncio.sleep(1)
+ callback(0)
+ await asyncio.sleep(2)
+
+# Test code
+from unittest.mock import Mock, call
+loop = asyncio.get_event_loop()
+callback = Mock()
+
+with aiofastforward.FastForward(loop) as forward:
+ asyncio.ensure_future(sleeper())
+
+ await forward(1) # Move time forward one second
+ self.assertEqual(callback.mock_calls, [])
+ await forward(1) # Move time forward another second
+ self.assertEqual(callback.mock_calls, [call(0)])
+```
+
+### loop.call_later
+
+```python
+# Production code
+async def schedule_callback(loop, callback):
+ loop.call_later(1, callback, 0)
+ loop.call_later(2, callback, 1)
+
+# Test code
+from unittest.mock import Mock, call
+loop = asyncio.get_event_loop()
+
+with aiofastforward.FastForward(loop) as forward:
+ callback = Mock()
+ await schedule_callback(loop, callback)
+
+ await forward(1) # Move time forward one second
+ self.assertEqual(callback.mock_calls, [call(0)])
+ await forward(1) # Move time forward another second
+ self.assertEqual(callback.mock_calls, [call(0), call(1)])
+```
+
+### loop.call_at
+
+```python
+# Production code
+async def schedule_callback(loop, callback):
+ now = loop.time()
+ loop.call_at(now + 1, callback, 0)
+ loop.call_at(now + 2, callback, 1)
+
+# Test code
+from unittest.mock import Mock, call
+loop = asyncio.get_event_loop()
+
+with aiofastforward.FastForward(loop) as forward:
+ callback = Mock()
+ await schedule_callback(loop, callback)
+
+ await forward(1) # Move time forward one second
+ self.assertEqual(callback.mock_calls, [call(0)])
+ await forward(1) # Move time forward another second
+ self.assertEqual(callback.mock_calls, [call(0), call(1)])
+```
+
+
+## `forward`ing time can block
+
+`await forward(a)` only moves time forward, i.e. resolve calls to `asyncio.sleep` or calls the callbacks of `call_at` or `call_later`, once there are sufficient such calls that time could have progressed that amount. Calls to IO functions, even if they take non-zero amounts of real time in the test, do not advance the patched "pseudo-timeline": they are treated as instantanous.
+
+This means that there are cases where `await forward(a)` will block forever.
+
+
+```python
+# Production code
+async def sleeper():
+ await asyncio.sleep(1)
+
+# Test code
+loop = asyncio.get_event_loop()
+
+with aiofastforward.FastForward(loop) as forward:
+ asyncio.ensure_future(sleeper())
+
+ await forward(2) # Will block forever
+```
+
+To avoid this, ensure you only `await forward` an amount less than or equal to how much pseudo-time that will be progressed by `asyncio.sleep`, `call_at` or `call_later`.
+
+```python
+# Production code
+async def sleeper(callback):
+ await asyncio.sleep(1)
+ callback(0)
+ await asyncio.sleep(1)
+ callback(1)
+
+# Test code
+from unittest.mock import Mock, call
+loop = asyncio.get_event_loop()
+
+with aiofastforward.FastForward(loop) as forward:
+ asyncio.ensure_future(sleeper(callback))
+ start_time = loop.time()
+
+ await forward(1.5) # The second sleep will have been called, but not resolved
+ self.assertEqual(loop.time(), start_time + 1.5)
+ self.assertEqual(callback.mock_calls, [call(0)])
+```
+
+The justification for this design are the consequences of the the alternative: if it _wouldn't_ block. This would mean that all sleeps and callbacks would have to be registered _before_ the call to `forward`, and this in turn would lead to less flexible test code.
+
+For example, the production code may have a chain of 10 `asyncio.sleep(1)`, and in the test you would like to `await forward(10)` to assert on the state of the system after these. At the time of calling `await forward(10)` however, at most one of the `asyncio.sleep(1)` would have been called. Not blocking would mean that after `await forward(10)`, the pseudo-timeline in the world of the patched production code would not have moved forward ten seconds.
+
+
+## Differences between aiofastforward.FastForward and [asynctest.ClockedTestCase](https://asynctest.readthedocs.io/en/latest/asynctest.case.html#asynctest.ClockedTestCase)
+
+There is overlap in functionality: both support fast-forwarding time in terms of loop.call_later and loop.call_at. However, there are properties that FastForward has that ClockedTestCase does not:
+
+- FastForward is not coupled to any particular test framework. The only requirement is that the test code must be in an async function. If you wish, you can use FastForward in an [asynctest.TestCase](https://asynctest.readthedocs.io/en/latest/asynctest.case.html#asynctest.TestCase) test.
+- FastForward supports fast-forwarding asyncio.sleep.
+- FastForward allows fast-forwarding time in any event loop, not just the one the test code runs in.
+
+ClockedTestCase does have an advantage over FastForward, which may be important for some uses:
+
+- ClockedTestCase supports Python 3.4 onwards, while FastForward supports Python 3.5.0 onwards.
+
+
+
+
+%prep
+%autosetup -n aiofastforward-0.0.24
+
+%build
+%py3_build
+
+%install
+%py3_install
+install -d -m755 %{buildroot}/%{_pkgdocdir}
+if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi
+if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi
+if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi
+if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi
+pushd %{buildroot}
+if [ -d usr/lib ]; then
+ find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/lib64 ]; then
+ find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/bin ]; then
+ find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/sbin ]; then
+ find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+touch doclist.lst
+if [ -d usr/share/man ]; then
+ find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst
+fi
+popd
+mv %{buildroot}/filelist.lst .
+mv %{buildroot}/doclist.lst .
+
+%files -n python3-aiofastforward -f filelist.lst
+%dir %{python3_sitelib}/*
+
+%files help -f doclist.lst
+%{_docdir}/*
+
+%changelog
+* Mon May 15 2023 Python_Bot <Python_Bot@openeuler.org> - 0.0.24-1
+- Package Spec generated
diff --git a/sources b/sources
new file mode 100644
index 0000000..9597726
--- /dev/null
+++ b/sources
@@ -0,0 +1 @@
+92a1c5eb6d5810d3a9237efed809fe7b aiofastforward-0.0.24.tar.gz