diff options
author | CoprDistGit <infra@openeuler.org> | 2023-05-05 15:21:10 +0000 |
---|---|---|
committer | CoprDistGit <infra@openeuler.org> | 2023-05-05 15:21:10 +0000 |
commit | e80a7b44e0113ff7a27e1ea8e3ef4a8e7e37b9e5 (patch) | |
tree | a87a03e32211f350f3305f127f625efa9532240a | |
parent | e445af88a0956ccb6eb2d7a137f7c0c8311cf131 (diff) |
automatic import of python-unsyncopeneuler20.03
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | python-unsync.spec | 594 | ||||
-rw-r--r-- | sources | 1 |
3 files changed, 596 insertions, 0 deletions
@@ -0,0 +1 @@ +/unsync-1.4.0.tar.gz diff --git a/python-unsync.spec b/python-unsync.spec new file mode 100644 index 0000000..c96fd9f --- /dev/null +++ b/python-unsync.spec @@ -0,0 +1,594 @@ +%global _empty_manifest_terminate_build 0 +Name: python-unsync +Version: 1.4.0 +Release: 1 +Summary: Unsynchronize asyncio +License: MIT +URL: https://github.com/alex-sherman/unsync +Source0: https://mirrors.nju.edu.cn/pypi/web/packages/ea/6b/8d9699d37fe8c43c76fda4a7f93ae6aef129576f75c3cacfc9a78a7cc984/unsync-1.4.0.tar.gz +BuildArch: noarch + + +%description +# unsync +Unsynchronize `asyncio` by using an ambient event loop, or executing in separate threads or processes. + +# Quick Overview + +Functions marked with the `@unsync` decorator will behave in one of the following ways: +* `async` functions will run in the `unsync.loop` event loop executed from `unsync.thread` +* Regular functions will execute in `unsync.thread_executor`, a `ThreadPoolExecutor` + * Useful for IO bounded work that does not support `asyncio` +* Regular functions marked with `@unsync(cpu_bound=True)` will execute in `unsync.process_executor`, a `ProcessPoolExecutor` + * Useful for CPU bounded work + +All `@unsync` functions will return an `Unfuture` object. +This new future type combines the behavior of `asyncio.Future` and `concurrent.Future` with the following changes: +* `Unfuture.set_result` is threadsafe unlike `asyncio.Future` +* `Unfuture` instances can be awaited, even if made from `concurrent.Future` +* `Unfuture.result()` is a blocking operation *except* in `unsync.loop`/`unsync.thread` where + it behaves like `asyncio.Future.result` and will throw an exception if the future is not done + +# Examples +## Simple Sleep +A simple sleeping example with `asyncio`: +```python +async def sync_async(): + await asyncio.sleep(1) + return 'I hate event loops' + + +async def main(): + future1 = asyncio.create_task(sync_async()) + future2 = asyncio.create_task(sync_async()) + + await future1, future2 + + print(future1.result() + future2.result()) + +asyncio.run(main()) +# Takes 1 second to run +``` + +Same example with `unsync`: +```python +@unsync +async def unsync_async(): + await asyncio.sleep(1) + return 'I like decorators' + +unfuture1 = unsync_async() +unfuture2 = unsync_async() +print(unfuture1.result() + unfuture2.result()) +# Takes 1 second to run +``` + +## Multi-threading an IO-bound function +Synchronous functions can be made to run asynchronously by executing them in a `concurrent.ThreadPoolExecutor`. +This can be easily accomplished by marking the regular function `@unsync`. +```python +@unsync +def non_async_function(seconds): + time.sleep(seconds) + return 'Run concurrently!' + +start = time.time() +tasks = [non_async_function(0.1) for _ in range(10)] +print([task.result() for task in tasks]) +print('Executed in {} seconds'.format(time.time() - start)) +``` +Which prints: + + ['Run concurrently!', 'Run concurrently!', ...] + Executed in 0.10807514190673828 seconds + +## Continuations +Using `Unfuture.then` chains asynchronous calls and returns an `Unfuture` that wraps both the source, and continuation. +The continuation is invoked with the source Unfuture as the first argument. +Continuations can be regular functions (which will execute synchronously), or `@unsync` functions. +```python +@unsync +async def initiate(request): + await asyncio.sleep(0.1) + return request + 1 + +@unsync +async def process(task): + await asyncio.sleep(0.1) + return task.result() * 2 + +start = time.time() +print(initiate(3).then(process).result()) +print('Executed in {} seconds'.format(time.time() - start)) +``` +Which prints: + + 8 + Executed in 0.20314741134643555 seconds + +## Mixing methods + +We'll start by converting a regular synchronous function into a threaded `Unfuture` which will begin our request. +```python +@unsync +def non_async_function(num): + time.sleep(0.1) + return num, num + 1 +``` +We may want to refine the result in another function, so we define the following continuation. +```python +@unsync +async def result_continuation(task): + await asyncio.sleep(0.1) + num, res = task.result() + return num, res * 2 +``` +We then aggregate all the results into a single dictionary in an async function. +```python +@unsync +async def result_processor(tasks): + output = {} + for task in tasks: + num, res = await task + output[num] = res + return output +``` +Executing the full chain of `non_async_function`→`result_continuation`→`result_processor` would look like: +```python +start = time.time() +print(result_processor([non_async_function(i).then(result_continuation) for i in range(10)]).result()) +print('Executed in {} seconds'.format(time.time() - start)) +``` + +Which prints: + + {0: 2, 1: 4, 2: 6, 3: 8, 4: 10, 5: 12, 6: 14, 7: 16, 8: 18, 9: 20} + Executed in 0.22115683555603027 seconds + +## Preserving typing +As far as we know it is not possible to change the return type of a method or function using a decorator. +Therefore, we need a workaround to properly use IntelliSense. You have three options in general: + +1. Ignore type warnings. +2. Use a suppression statement where you reach the type warning. + + A. When defining the unsynced method by changing the return type to an `Unfuture`. + + B. When using the unsynced method. + +3. Wrap the function without a decorator. Example: + ```python + def function_name(x: str) -> Unfuture[str]: + async_method = unsync(__function_name_synced) + return async_method(x) + + def __function_name_synced(x: str) -> str: + return x + 'a' + + future_result = function_name('b') + self.assertEqual('ba', future_result.result()) + ``` + +## Custom Event Loops +In order to use custom event loops, be sure to set the event loop policy before calling any `@unsync` methods. +For example, to use `uvloop` simply: + +```python +import unsync +import uvloop + +@unsync +async def main(): + # Main entry-point. + ... + +uvloop.install() # Equivalent to asyncio.set_event_loop_policy(EventLoopPolicy()) +main() +``` + +%package -n python3-unsync +Summary: Unsynchronize asyncio +Provides: python-unsync +BuildRequires: python3-devel +BuildRequires: python3-setuptools +BuildRequires: python3-pip +%description -n python3-unsync +# unsync +Unsynchronize `asyncio` by using an ambient event loop, or executing in separate threads or processes. + +# Quick Overview + +Functions marked with the `@unsync` decorator will behave in one of the following ways: +* `async` functions will run in the `unsync.loop` event loop executed from `unsync.thread` +* Regular functions will execute in `unsync.thread_executor`, a `ThreadPoolExecutor` + * Useful for IO bounded work that does not support `asyncio` +* Regular functions marked with `@unsync(cpu_bound=True)` will execute in `unsync.process_executor`, a `ProcessPoolExecutor` + * Useful for CPU bounded work + +All `@unsync` functions will return an `Unfuture` object. +This new future type combines the behavior of `asyncio.Future` and `concurrent.Future` with the following changes: +* `Unfuture.set_result` is threadsafe unlike `asyncio.Future` +* `Unfuture` instances can be awaited, even if made from `concurrent.Future` +* `Unfuture.result()` is a blocking operation *except* in `unsync.loop`/`unsync.thread` where + it behaves like `asyncio.Future.result` and will throw an exception if the future is not done + +# Examples +## Simple Sleep +A simple sleeping example with `asyncio`: +```python +async def sync_async(): + await asyncio.sleep(1) + return 'I hate event loops' + + +async def main(): + future1 = asyncio.create_task(sync_async()) + future2 = asyncio.create_task(sync_async()) + + await future1, future2 + + print(future1.result() + future2.result()) + +asyncio.run(main()) +# Takes 1 second to run +``` + +Same example with `unsync`: +```python +@unsync +async def unsync_async(): + await asyncio.sleep(1) + return 'I like decorators' + +unfuture1 = unsync_async() +unfuture2 = unsync_async() +print(unfuture1.result() + unfuture2.result()) +# Takes 1 second to run +``` + +## Multi-threading an IO-bound function +Synchronous functions can be made to run asynchronously by executing them in a `concurrent.ThreadPoolExecutor`. +This can be easily accomplished by marking the regular function `@unsync`. +```python +@unsync +def non_async_function(seconds): + time.sleep(seconds) + return 'Run concurrently!' + +start = time.time() +tasks = [non_async_function(0.1) for _ in range(10)] +print([task.result() for task in tasks]) +print('Executed in {} seconds'.format(time.time() - start)) +``` +Which prints: + + ['Run concurrently!', 'Run concurrently!', ...] + Executed in 0.10807514190673828 seconds + +## Continuations +Using `Unfuture.then` chains asynchronous calls and returns an `Unfuture` that wraps both the source, and continuation. +The continuation is invoked with the source Unfuture as the first argument. +Continuations can be regular functions (which will execute synchronously), or `@unsync` functions. +```python +@unsync +async def initiate(request): + await asyncio.sleep(0.1) + return request + 1 + +@unsync +async def process(task): + await asyncio.sleep(0.1) + return task.result() * 2 + +start = time.time() +print(initiate(3).then(process).result()) +print('Executed in {} seconds'.format(time.time() - start)) +``` +Which prints: + + 8 + Executed in 0.20314741134643555 seconds + +## Mixing methods + +We'll start by converting a regular synchronous function into a threaded `Unfuture` which will begin our request. +```python +@unsync +def non_async_function(num): + time.sleep(0.1) + return num, num + 1 +``` +We may want to refine the result in another function, so we define the following continuation. +```python +@unsync +async def result_continuation(task): + await asyncio.sleep(0.1) + num, res = task.result() + return num, res * 2 +``` +We then aggregate all the results into a single dictionary in an async function. +```python +@unsync +async def result_processor(tasks): + output = {} + for task in tasks: + num, res = await task + output[num] = res + return output +``` +Executing the full chain of `non_async_function`→`result_continuation`→`result_processor` would look like: +```python +start = time.time() +print(result_processor([non_async_function(i).then(result_continuation) for i in range(10)]).result()) +print('Executed in {} seconds'.format(time.time() - start)) +``` + +Which prints: + + {0: 2, 1: 4, 2: 6, 3: 8, 4: 10, 5: 12, 6: 14, 7: 16, 8: 18, 9: 20} + Executed in 0.22115683555603027 seconds + +## Preserving typing +As far as we know it is not possible to change the return type of a method or function using a decorator. +Therefore, we need a workaround to properly use IntelliSense. You have three options in general: + +1. Ignore type warnings. +2. Use a suppression statement where you reach the type warning. + + A. When defining the unsynced method by changing the return type to an `Unfuture`. + + B. When using the unsynced method. + +3. Wrap the function without a decorator. Example: + ```python + def function_name(x: str) -> Unfuture[str]: + async_method = unsync(__function_name_synced) + return async_method(x) + + def __function_name_synced(x: str) -> str: + return x + 'a' + + future_result = function_name('b') + self.assertEqual('ba', future_result.result()) + ``` + +## Custom Event Loops +In order to use custom event loops, be sure to set the event loop policy before calling any `@unsync` methods. +For example, to use `uvloop` simply: + +```python +import unsync +import uvloop + +@unsync +async def main(): + # Main entry-point. + ... + +uvloop.install() # Equivalent to asyncio.set_event_loop_policy(EventLoopPolicy()) +main() +``` + +%package help +Summary: Development documents and examples for unsync +Provides: python3-unsync-doc +%description help +# unsync +Unsynchronize `asyncio` by using an ambient event loop, or executing in separate threads or processes. + +# Quick Overview + +Functions marked with the `@unsync` decorator will behave in one of the following ways: +* `async` functions will run in the `unsync.loop` event loop executed from `unsync.thread` +* Regular functions will execute in `unsync.thread_executor`, a `ThreadPoolExecutor` + * Useful for IO bounded work that does not support `asyncio` +* Regular functions marked with `@unsync(cpu_bound=True)` will execute in `unsync.process_executor`, a `ProcessPoolExecutor` + * Useful for CPU bounded work + +All `@unsync` functions will return an `Unfuture` object. +This new future type combines the behavior of `asyncio.Future` and `concurrent.Future` with the following changes: +* `Unfuture.set_result` is threadsafe unlike `asyncio.Future` +* `Unfuture` instances can be awaited, even if made from `concurrent.Future` +* `Unfuture.result()` is a blocking operation *except* in `unsync.loop`/`unsync.thread` where + it behaves like `asyncio.Future.result` and will throw an exception if the future is not done + +# Examples +## Simple Sleep +A simple sleeping example with `asyncio`: +```python +async def sync_async(): + await asyncio.sleep(1) + return 'I hate event loops' + + +async def main(): + future1 = asyncio.create_task(sync_async()) + future2 = asyncio.create_task(sync_async()) + + await future1, future2 + + print(future1.result() + future2.result()) + +asyncio.run(main()) +# Takes 1 second to run +``` + +Same example with `unsync`: +```python +@unsync +async def unsync_async(): + await asyncio.sleep(1) + return 'I like decorators' + +unfuture1 = unsync_async() +unfuture2 = unsync_async() +print(unfuture1.result() + unfuture2.result()) +# Takes 1 second to run +``` + +## Multi-threading an IO-bound function +Synchronous functions can be made to run asynchronously by executing them in a `concurrent.ThreadPoolExecutor`. +This can be easily accomplished by marking the regular function `@unsync`. +```python +@unsync +def non_async_function(seconds): + time.sleep(seconds) + return 'Run concurrently!' + +start = time.time() +tasks = [non_async_function(0.1) for _ in range(10)] +print([task.result() for task in tasks]) +print('Executed in {} seconds'.format(time.time() - start)) +``` +Which prints: + + ['Run concurrently!', 'Run concurrently!', ...] + Executed in 0.10807514190673828 seconds + +## Continuations +Using `Unfuture.then` chains asynchronous calls and returns an `Unfuture` that wraps both the source, and continuation. +The continuation is invoked with the source Unfuture as the first argument. +Continuations can be regular functions (which will execute synchronously), or `@unsync` functions. +```python +@unsync +async def initiate(request): + await asyncio.sleep(0.1) + return request + 1 + +@unsync +async def process(task): + await asyncio.sleep(0.1) + return task.result() * 2 + +start = time.time() +print(initiate(3).then(process).result()) +print('Executed in {} seconds'.format(time.time() - start)) +``` +Which prints: + + 8 + Executed in 0.20314741134643555 seconds + +## Mixing methods + +We'll start by converting a regular synchronous function into a threaded `Unfuture` which will begin our request. +```python +@unsync +def non_async_function(num): + time.sleep(0.1) + return num, num + 1 +``` +We may want to refine the result in another function, so we define the following continuation. +```python +@unsync +async def result_continuation(task): + await asyncio.sleep(0.1) + num, res = task.result() + return num, res * 2 +``` +We then aggregate all the results into a single dictionary in an async function. +```python +@unsync +async def result_processor(tasks): + output = {} + for task in tasks: + num, res = await task + output[num] = res + return output +``` +Executing the full chain of `non_async_function`→`result_continuation`→`result_processor` would look like: +```python +start = time.time() +print(result_processor([non_async_function(i).then(result_continuation) for i in range(10)]).result()) +print('Executed in {} seconds'.format(time.time() - start)) +``` + +Which prints: + + {0: 2, 1: 4, 2: 6, 3: 8, 4: 10, 5: 12, 6: 14, 7: 16, 8: 18, 9: 20} + Executed in 0.22115683555603027 seconds + +## Preserving typing +As far as we know it is not possible to change the return type of a method or function using a decorator. +Therefore, we need a workaround to properly use IntelliSense. You have three options in general: + +1. Ignore type warnings. +2. Use a suppression statement where you reach the type warning. + + A. When defining the unsynced method by changing the return type to an `Unfuture`. + + B. When using the unsynced method. + +3. Wrap the function without a decorator. Example: + ```python + def function_name(x: str) -> Unfuture[str]: + async_method = unsync(__function_name_synced) + return async_method(x) + + def __function_name_synced(x: str) -> str: + return x + 'a' + + future_result = function_name('b') + self.assertEqual('ba', future_result.result()) + ``` + +## Custom Event Loops +In order to use custom event loops, be sure to set the event loop policy before calling any `@unsync` methods. +For example, to use `uvloop` simply: + +```python +import unsync +import uvloop + +@unsync +async def main(): + # Main entry-point. + ... + +uvloop.install() # Equivalent to asyncio.set_event_loop_policy(EventLoopPolicy()) +main() +``` + +%prep +%autosetup -n unsync-1.4.0 + +%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-unsync -f filelist.lst +%dir %{python3_sitelib}/* + +%files help -f doclist.lst +%{_docdir}/* + +%changelog +* Fri May 05 2023 Python_Bot <Python_Bot@openeuler.org> - 1.4.0-1 +- Package Spec generated @@ -0,0 +1 @@ +6b2119ba4a7e2795dbb544e0cd00705b unsync-1.4.0.tar.gz |