├── .github └── workflows │ └── python-package.yml ├── LICENSE ├── README-CN.md ├── README.md ├── demo.py ├── goroutine ├── __init__.py └── app.py └── requirements.txt /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: Python package 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v3 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | python -m pip install flake8 goroutine-py 31 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 32 | - name: Lint with flake8 33 | run: | 34 | # stop the build if there are Python syntax errors or undefined names 35 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 36 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 37 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 38 | - name: Test demo 39 | run: | 40 | python demo.py 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 ZIHAN MA 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README-CN.md: -------------------------------------------------------------------------------- 1 | [English](https://github.com/purplegrapeZz/goroutine-py/blob/master/README.md) | 中文 2 | 3 | ### goroutine-py 4 | 5 | 🚀 基于Asyncio的Python并发库. 6 | 7 | ​ 在Python中像goroutine一样使用线程和协程. 8 | 9 | # 介绍 10 | 11 | 您可以通过 ``goroutine.app.go`` 异步地使用线程和协程. 12 | 13 | 主函数 ___go___ : 14 | 15 | ##### go _(obj: callable, *args, callback: callable = None, lock: bool = False)_ 16 | 17 | ​ ___obj:___ 接受协程方法或普通方法. 18 | 19 | ​ ___*args:___ 任务方法需要的参数. 20 | 21 | ​ ___callback:___ 任务结束后的回调函数. 22 | 23 | ​ ___lock:___ 线程安全参数, 只在线程(传入普通函数)中有效. 24 | 25 | 26 | 27 | # 开始 28 | ## 支持 29 | 30 | Python 3.7 / 3.8 / 3.9 / 3.10 / 3.11 / 3.12 31 | 32 | ## 安装 33 | 34 | 安装 goroutine-py : 35 | 36 | ``` 37 | pip install goroutine-py 38 | ``` 39 | 40 | ## 教程 41 | 42 | goroutine-py 的主要函数是 ``goroutine.app.go``. 43 | 简单两步学会使用goroutine-py: 44 | 45 | 首先, 定义你的任务函数: 46 | 47 | ``` 48 | import asyncio 49 | import functools 50 | import time 51 | from goroutine.app import go 52 | 53 | # 普通函数 54 | def task_1(n=2): 55 | time.sleep(n) 56 | print('Task_1_done') 57 | return 'Result_1' 58 | ``` 59 | 60 | ``` 61 | # 协程函数 62 | async def task_2(n=1): 63 | await asyncio.sleep(n) 64 | print('Task_2_done') 65 | return 'Result_2' 66 | ``` 67 | 68 | ``` 69 | # 回调函数 70 | def callback(future, arg=None): 71 | ''' 72 | 至少需要声明一个 "future" 参数. 73 | future 是 concurrent.futures.Future. 74 | 使用 functools.partial() 为回调函数传递参数. 75 | ''' 76 | print(future.result(),arg) 77 | ``` 78 | go分配你的任务 : 79 | 80 | ``` 81 | go(task_1) 82 | go(task_2) 83 | go(task_1, 4, callback=callback) 84 | go(task_2, 2, callback=functools.partial(callback,arg='a')) 85 | print('END') 86 | ``` 87 | 88 | 输出 : 89 | 90 | ``` 91 | >>> 92 | END 93 | Task_2_done 94 | Task_1_done 95 | Task_2_done 96 | Result_2 a 97 | Task_1_done 98 | Result_1 None 99 | ``` 100 | 101 | # 执照 102 | 103 | 使用 MIT License - 详情参见 `LICENSE` 文件. 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | English | 中文 2 | 3 | goroutine-py 4 | 5 | 🚀 An Asyncio-based concurrency library for Python. 6 | 7 | Easy concurrency just like goroutine without worry about thread and coroutine in Python. 8 | 9 | 10 | 11 | Introduction 12 | 13 | Withing goroutine.app.go you can run a coroutine or a func asynchronously. 14 | 15 | Main function go : 16 | 17 | go (obj: callable, *args, callback: callable = None, lock: bool = False) 18 | 19 | obj: Takes both callable coroutinefunction and func as object. 20 | 21 | *args: Arguments for your obj. 22 | 23 | callback: Attaches a callable that will be called when the future finishes. 24 | 25 | 26 | 27 | Getting Started 28 | 29 | Support: 30 | 31 | Python 3.7 / 3.8 / 3.9 / 3.10 / 3.11 / 3.12 32 | 33 | Installation 34 | 35 | First you have to install goroutine-py like this: 36 | 37 | pip install goroutine-py 38 | 39 | Quick Tutorial 40 | 41 | The primary entity of goroutine-py is goroutine.app.go. 42 | 43 | You can simply start using goroutine-py like this: 44 | 45 | First, define your tasks: 46 | 47 | import asyncio 48 | import time 49 | from goroutine.app import go 50 | 51 | # A normal func 52 | def task_1(n=2): 53 | time.sleep(n) 54 | print('Task_1_done') 55 | return 'Result_1' 56 | 57 | # A coroutinefunction 58 | async def task_2(n=1): 59 | await asyncio.sleep(n) 60 | print('Task_2_done') 61 | return 'Result_2' 62 | 63 | # Callback func 64 | def callback(result): 65 | ''' 66 | Parameter "result" is the return from task. 67 | Use functools.partial() to give arguments if you need more args at the beginning. 68 | ''' 69 | print('-* callback *-') 70 | print(result) 71 | 72 | After you defined all your tasks and callback, you can go like this: 73 | 74 | go(task_1) 75 | go(task_2) 76 | 77 | # The "callback" parameter must be specified separately. 78 | go(task_1, 5, callback = callback) 79 | go(task_2, 3, callback = callback) 80 | print('END') 81 | 82 | # Forever runing to show results. 83 | while 1: 84 | time.sleep(5) 85 | 86 | Output : 87 | 88 | >>> 89 | END 90 | Task_2_done 91 | Task_1_done 92 | Task_2_done 93 | -* callback *- 94 | Result_2 95 | Task_1_done 96 | -* callback *- 97 | Result_1 98 | 99 | License 100 | 101 | This project is licensed under the MIT License - see the LICENSE file for details. 102 | -------------------------------------------------------------------------------- /demo.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import time 3 | from goroutine.app import go 4 | 5 | 6 | # A normal func 7 | def task_1(n=2): 8 | time.sleep(n) 9 | print('Task_1_done') 10 | return 'Result_1' 11 | 12 | # A coroutinefunction 13 | async def task_2(n=1): 14 | await asyncio.sleep(n) 15 | print('Task_2_done') 16 | return 'Result_2' 17 | 18 | # Callback func 19 | def callback(result): 20 | ''' 21 | Parameter "result" is the return from task. 22 | Use functools.partial() to give arguments if you need more args at the beginning. 23 | ''' 24 | print('-* callback *-') 25 | print(result) 26 | 27 | 28 | if __name__ == '__main__': 29 | go(task_1) 30 | go(task_2) 31 | 32 | # The "callback" parameter must be specified separately. 33 | go(task_1, 5, callback = callback) 34 | go(task_2, 3, callback = callback) 35 | print('END') 36 | 37 | # Forever runing to show results. 38 | while 1: 39 | time.sleep(5) 40 | 41 | -------------------------------------------------------------------------------- /goroutine/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 ZIHAN MA. All Rights Reserved. 2 | 3 | ''' 4 | 🚀 An Asyncio-based concurrency library for Python. 5 | Easy concurrency in Python. Just like goroutine. 6 | ''' 7 | 8 | __author__ = 'ZIHAN MA (xudesoft@126.com)' 9 | 10 | def __dir__(): 11 | return __doc__ 12 | -------------------------------------------------------------------------------- /goroutine/app.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 ZIHAN MA. All Rights Reserved. 2 | 3 | ''' 4 | 🚀 An Asyncio-based concurrency library for Python. 5 | Easy concurrency in Python. Just like goroutine. 6 | ''' 7 | 8 | __author__ = 'ZIHAN MA (xudesoft@126.com)' 9 | 10 | import asyncio 11 | from threading import Thread 12 | from typing import Any 13 | 14 | def _run() -> None: 15 | ''' 16 | Start loop. 17 | ''' 18 | _goroutine_loop.run_forever() 19 | 20 | 21 | def _iscor(obj: callable) -> bool: 22 | ''' 23 | Check if obj a coroutinefunction. 24 | Return: 25 | True or Fale. 26 | ''' 27 | return asyncio.iscoroutinefunction(obj) 28 | 29 | 30 | async def _wrap_cor(_cor, *args, callback = None) -> None: 31 | ''' 32 | Handle the obj. 33 | And will put this wrapper to the loop. 34 | Return: 35 | None. 36 | ''' 37 | res = await _cor 38 | 39 | # Handle the callback func from user. 40 | if callable(callback): 41 | if _iscor(callback): 42 | await callback(res) if res else callback() 43 | else: 44 | await _wrap_func(callback, res) if res else _wrap_func(callback) 45 | 46 | 47 | async def _wrap_func(func: callable, *args) -> Any: 48 | ''' 49 | For normal func, run as a thread. 50 | Return: 51 | Result of func. 52 | ''' 53 | res = await asyncio.to_thread(func, *args) 54 | return res 55 | 56 | 57 | def go(obj: callable, *args, callback: callable = None) -> None: 58 | ''' 59 | Run a coroutine or a func asynchronously. 60 | Easy concurrency in Python. 61 | Args: 62 | obj: Takes both callable coroutinefunction and func as object. 63 | Coroutinefunction runs as coroutine. 64 | Normal function runs as thread. 65 | *args: Arguments for your obj. 66 | You can also use functools.partial() for your func. 67 | callback: Attaches a callable that will be called when the cor finishes. 68 | Return: 69 | None 70 | ''' 71 | if callable(obj): 72 | # Check if the given obeject callable. 73 | if _iscor(obj): 74 | # If a coroutinefunction run this. 75 | future = asyncio.run_coroutine_threadsafe(_wrap_cor(obj(*args), callback=callback), _goroutine_loop) 76 | else: 77 | # If normal func, runs as a thread. 78 | future = asyncio.run_coroutine_threadsafe(_wrap_cor(_wrap_func(obj, *args), callback=callback), _goroutine_loop) 79 | else: 80 | raise TypeError( 81 | 'A callable func or coroutinefunction object is required') 82 | 83 | 84 | # Getting loop. 85 | _goroutine_loop = asyncio.new_event_loop() 86 | 87 | # Run the loop in a thread. 88 | T = Thread(target=_run, daemon=True) 89 | T.start() 90 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | goroutine-py --------------------------------------------------------------------------------