Compare commits

...

14 Commits
v1.0.1 ... main

12 changed files with 527 additions and 72 deletions

View File

@ -3,7 +3,7 @@ name: Publish
on:
push:
tags:
- '*'
- 'v*'
jobs:
pypi-publish:

3
.gitignore vendored
View File

@ -160,3 +160,6 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/
# Sublime Text
result.json

134
README.md
View File

@ -1,27 +1,139 @@
# magicoca
A communication library for Python
# Magicoca
![Logo](./logo.png)
## 支持的通信方式
- 进程通信
## :art: 简介
Magicoca是一个基于MIT协议的简易Python进程通信库提供了一种简单的方式来实现进程间通信。
## 稀奇古怪的语法
## :rocket: 安装
使用pip安装
```bash
pip install magicoca
```
或是使用源码安装(不推荐)
1. 将仓库clone到本地
> 你需要首先安装[Git](https://git-scm.com/)
```bash
git clone https://github.com/LiteyukiStudio/magicoca
```
> 如果你的网络环境受到限制或影响, 也可以尝试使用Liteyuki Gitea镜像仓库:
> ```bash
> https://git.liteyuki.icu/bot/magicoca
> ```
2. 进入仓库目录(取决于你的文件夹路径)
```bash
cd magicoca
```
3. 使用pip本地安装
```bash
pip install -e .
```
## :hammer: 使用
```python
from magicoca.chan import Chan
# 创建一个通道
ch = Chan()
# 发送消息
ch << "hello"
# 接受消息
msg = str << ch
# 通道关闭
ch.close()
```
## :book: 文档
### 通道
通道是一个用于进程间通信的对象。
如:
```python
from magicoca.chan import Chan
ch = Chan()
```
这里的`ch`就是一个通道。
# 发送消息
### 发送消息
使用`<<`操作符可以将消息发送到通道。
如:
```python
ch << "hello"
```
这里的`"hello"`就是一个消息。
# 接受消息
当然你也可以发送其他类型的消息,比如数字、列表等。
### 接受消息
使用`>>`操作符可以从通道中接受消息。
如:
```python
msg = str << ch
```
这里的`msg`就是一个消息。
# 通道关闭
当然你也可以接受其他类型的消息,比如数字、列表等。
### 通道关闭
使用`close`方法可以关闭通道。
如:
```python
ch.close()
```
当通道关闭后,你就不能再向通道中发送消息了。
# 可跨进程通信
```
### 通道状态
你可以使用`is_closed`方法来判断通道是否关闭。
如:
```python
if ch.is_closed():
print("通道已关闭")
else:
print("通道未关闭")
```
或者基于该原理, 你可以使用`if not ch.is_closed():`来判断通道是否未关闭来间接性发送消息并随时关闭通道以避免出现可能的错误。
### 通道容量
实际上Magicoca的通道是没有容量限制的你可以发送任意数量的消息。
当然, 前提是你的机器内存足够大。
### 通道类型
通道的类型是由你自己定义的。
如:
```python
from magicoca.chan import Chan
# 创建一个通道, 类型为str
ch = Chan(str)
```
如果不指定通道类型, 那么通道的类型将默认为`Any`
### 通道超时
通道的超时是指在接受消息时, 如果没有消息, 那么就会等待一段时间, 直到有消息或者超时。
如:
```python
from magicoca.chan import Chan
# 创建一个通道, 类型为str, 超时时间为1秒
ch = Chan(str, timeout=1)
```
如果不指定超时时间, 那么通道的超时时间将默认为`None`
即无限等待。
### 通道支持
支持线程间通信。

BIN
logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

View File

@ -0,0 +1,46 @@
from multiprocessing import set_start_method
from multiprocessing.connection import wait, Connection
from typing import Generator
from magicoca.chan import Chan, T, NoRecvValue
__all__ = [
"Chan",
"select"
]
set_start_method("spawn", force=True)
def select(*args: Chan[T]) -> Generator[T, None, None]:
"""
When one of the channels receives data, yield that data.
当其中一个通道接收到数据时, yield 该数据
Args:
args: Multiple Chan objects
多个 Chan 对象
Returns:
Generator[T, None, None]: A generator that yields data from the channels.
一个生成器, 用于从通道中 yield 数据
"""
# 构造管道到通道列表的映射,避免重复的 recv_conn 对象
pipe_to_chs: dict[Connection, list[Chan[T]]] = {}
for ch in args:
if not ch.is_closed:
pipe: Connection = ch.recv_conn
pipe_to_chs.setdefault(pipe, []).append(ch)
pipes: list[Connection] = list(pipe_to_chs.keys())
while pipes:
ready_pipes: list[Connection] = wait(pipes) # type: ignore
for pipe in ready_pipes:
# 遍历所有使用该管道的通道
channels: list[Chan[T]] = list(pipe_to_chs.get(pipe, []))
for ch in channels:
if not isinstance(value := ch.recv(0), NoRecvValue):
yield value
if ch.is_closed:
pipe_to_chs[pipe].remove(ch)
# 如果该管道已没有活跃的通道,则移除
if not pipe_to_chs[pipe]:
pipes.remove(pipe)

View File

@ -1,49 +1,149 @@
"""
Chan is a simple channel implementation using multiprocessing.Pipe.
Chan 是一个简单的通道实现使用 multiprocessing.Pipe
"""
from multiprocessing import Pipe
from typing import TypeVar, Generic, Any
T = TypeVar("T")
class NoRecvValue(Exception):
"""
Exception raised when there is no value to receive.
当没有值可以接收时引发的异常
"""
pass
class Chan(Generic[T]):
def __init__(self, buffer: int = 0):
self._buffer = buffer
self._send_conn, self._recv_conn = Pipe()
"""
Chan is a simple channel implementation using multiprocessing.Pipe.
Chan 是一个简单的通道实现使用 multiprocessing.Pipe
Attributes:
send_conn: The sending end of the pipe.
管道的发送端
recv_conn: The receiving end of the pipe.
管道的接收端
"""
def __init__(self):
"""
Initialize the channel.
初始化通道
"""
self.send_conn, self.recv_conn = Pipe()
self.is_closed = False
def send(self, value: T):
self._send_conn.send(value)
def recv(self) -> T:
return self._recv_conn.recv()
def close(self):
self._send_conn.close()
self._recv_conn.close()
def __iter__(self):
"""
Send a value to the channel.
发送一个值到通道
Args:
value: The value to send.
要发送的值
"""
self.send_conn.send(value)
def recv(self, timeout: float | None = None) -> T | NoRecvValue:
"""
Receive a value from the channel.
从通道接收一个值
Args:
timeout: The maximum time to wait for a value.
等待值的最长时间
If None, it will block until a value is received.
如果为 None则阻塞直到接收到值
Returns:
T: The value received from the channel.
从通道接收的值
"""
if timeout is not None:
if not self.recv_conn.poll(timeout):
return NoRecvValue("No value to receive.")
return self.recv_conn.recv()
def close(self):
"""
Close the channel.
关闭通道
"""
self.send_conn.close()
self.recv_conn.close()
self.is_closed = True
def __iter__(self) -> "Chan[T]":
"""
Return the channel object.
返回通道对象
"""
return self
def __next__(self) -> T:
def __next__(self) -> T | NoRecvValue:
"""
Receive a value from the channel.
从通道接收一个值
"""
return self.recv()
def __lshift__(self, other: T):
"""
chan << obj
Args:
other:
Returns:
Send an object to the channel.
发送一个对象到通道
Args:
other: The object to send.
要发送的对象
Returns:
self: The channel object.
通道对象
"""
self.send(other)
return self
def __rlshift__(self, other: Any) -> T:
def __rlshift__(self, other: Any) -> T | NoRecvValue:
"""
<< chan
Returns:
Receive a value from the channel.
从通道接收一个值
Returns:
T: The value received from the channel.
从通道接收的值
"""
return self.recv()
return self.recv(None)
def __add__(self, other: "Chan[T]"):
"""
Connect one channel's send to another channel's recv.
将一个通道的发送端连接到另一个通道的接收端
Args:
other: The other channel to connect.
要连接的另一个通道
"""
self.recv_conn, other.recv_conn = other.recv_conn, self.recv_conn
def __del__(self):
"""
Close the channel when the object is deleted.
当对象被删除时关闭通道
"""
self.close()

0
magicoca/py.typed Normal file
View File

37
main.py Normal file
View File

@ -0,0 +1,37 @@
import time
from multiprocessing import Process
from magicoca import select, Chan
def send_process(chan: Chan[int], _id: int):
i = 0
while True:
i += 1
chan << _id
time.sleep(2)
if i == 50:
chan << -1
break
def recv_process(chan_list: list[Chan[int]]):
for t in select(*chan_list):
print(t)
if t == -1:
break
def main():
chan_list = []
for i in range(10):
chan = Chan[int]()
chan_list.append(chan)
p = Process(target=send_process, args=(chan, i))
p.start()
p = Process(target=recv_process, args=(chan_list,))
p.start()
if __name__ == '__main__':
main()

114
pdm.lock generated
View File

@ -5,7 +5,7 @@
groups = ["default", "dev"]
strategy = ["inherit_metadata"]
lock_version = "4.5.0"
content_hash = "sha256:7448dc54658d779bbdd51b39ae72faf632280702d8719337a0fff377415332ba"
content_hash = "sha256:1a5afca1143365cebc4c8dbe2a7d96e08a78ff461030ba0f0fd92d9b2e1b3771"
[[metadata.targets]]
requires_python = ">=3.10"
@ -45,6 +45,59 @@ files = [
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
]
[[package]]
name = "mypy"
version = "1.11.2"
requires_python = ">=3.8"
summary = "Optional static typing for Python"
groups = ["dev"]
dependencies = [
"mypy-extensions>=1.0.0",
"tomli>=1.1.0; python_version < \"3.11\"",
"typing-extensions>=4.6.0",
]
files = [
{file = "mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a"},
{file = "mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef"},
{file = "mypy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41ea707d036a5307ac674ea172875f40c9d55c5394f888b168033177fce47383"},
{file = "mypy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6e658bd2d20565ea86da7d91331b0eed6d2eee22dc031579e6297f3e12c758c8"},
{file = "mypy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:478db5f5036817fe45adb7332d927daa62417159d49783041338921dcf646fc7"},
{file = "mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385"},
{file = "mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca"},
{file = "mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104"},
{file = "mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4"},
{file = "mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6"},
{file = "mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318"},
{file = "mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36"},
{file = "mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987"},
{file = "mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca"},
{file = "mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70"},
{file = "mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12"},
{file = "mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79"},
]
[[package]]
name = "mypy-extensions"
version = "1.0.0"
requires_python = ">=3.5"
summary = "Type system extensions for programs checked with the mypy type checker."
groups = ["dev"]
files = [
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
]
[[package]]
name = "objprint"
version = "0.2.3"
requires_python = ">=3.6"
summary = "A library that can print Python objects in human readable format"
groups = ["dev"]
files = [
{file = "objprint-0.2.3-py3-none-any.whl", hash = "sha256:1721e6f97bae5c5b86c2716a0d45a9dd2c9a4cd9f52cfc8a0dfbe801805554cb"},
{file = "objprint-0.2.3.tar.gz", hash = "sha256:73d0ad5a7c3151fce634c8892e5c2a050ccae3b1a353bf1316f08b7854da863b"},
]
[[package]]
name = "packaging"
version = "24.1"
@ -97,3 +150,62 @@ files = [
{file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"},
{file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"},
]
[[package]]
name = "typing-extensions"
version = "4.12.2"
requires_python = ">=3.8"
summary = "Backported and Experimental Type Hints for Python 3.8+"
groups = ["dev"]
files = [
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
]
[[package]]
name = "viztracer"
version = "0.17.0"
requires_python = ">=3.9"
summary = "A debugging and profiling tool that can trace and visualize python code execution"
groups = ["dev"]
dependencies = [
"objprint>0.1.3",
]
files = [
{file = "viztracer-0.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c963fca8b32b8f34cfb931836d2214a0939503692ba12f7e7c883e89be558a5"},
{file = "viztracer-0.17.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:486990cd0f5761dbe6c88c6fb4e2ff72b2e4b60f9bddfbf692973268b6d5879f"},
{file = "viztracer-0.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2658dedb31119031d75e9dc82c55a8b6a2d6e4075a6af9afa765718ae8d2bad1"},
{file = "viztracer-0.17.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7af07a223e25ec2ada6b7f8a0a4ebaa4ac4120c25910df470d7a85a426e9117d"},
{file = "viztracer-0.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b6b7a42bca2eac521afadd33f513fefb08099b8f7dd08fb346d20e012b0fdcf"},
{file = "viztracer-0.17.0-cp310-cp310-win32.whl", hash = "sha256:c558853385bea8d70735fd36c75a35f37dad99fd3de2064fc9f709046312730f"},
{file = "viztracer-0.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:455e2cc6f6d69d0caaa20f13217b140070531c3ec35eb6878e7a37ee107acd6b"},
{file = "viztracer-0.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:13b207badfeaa89096c285d7161b4d83db41c7f7721dcec0091e5426a47d636a"},
{file = "viztracer-0.17.0-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:d44133a2279266238a3ebdcef00ab9a89f4e7f0596521166d25e5400ed6207ae"},
{file = "viztracer-0.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:929094255fb0bc7de1e415a79a2c5c6fc3c71fc101818c5729991ebd25f89ed1"},
{file = "viztracer-0.17.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c98deafdb3439a8cb41b1d5eba8846c5e8c672dac757cead7ecaa2c7e240177"},
{file = "viztracer-0.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad758e75f7b5fc8ffb45725f21f4dd1bd2787c0fe80b079ac1dee3b779069c3"},
{file = "viztracer-0.17.0-cp311-cp311-win32.whl", hash = "sha256:28f38c54db3957b91c582b90b6090ce7c9b693d73d2f2320ebd02e996303d5d0"},
{file = "viztracer-0.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:371742b31ca2cbfecefa6fcdbc84cfff798c43f7497d57b0d9cb2e3690083486"},
{file = "viztracer-0.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:67784cb54311f5580ea14adf05988bc9f2c180b896d541cea482062fc0495916"},
{file = "viztracer-0.17.0-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:3b86ff18e479239bde2bafdf1035d4c3ead8185bdca7446bdb076c13a10dad81"},
{file = "viztracer-0.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f56fa71aa8ba44870fa35b3b443b0fb49c91dd1ae0d900db714364d26637be3"},
{file = "viztracer-0.17.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:912ad33d2435797ca0e09c2e044d0d6538ccbadb537b71f4eb2cf27c8910c4d8"},
{file = "viztracer-0.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b01147907c805de78fe4bfaadac3e1fb9f17adb88b30afa6500bf73f18b4d042"},
{file = "viztracer-0.17.0-cp312-cp312-win32.whl", hash = "sha256:21c00b5b97b9b7ce5afea6288de3234457bdae8aa123df442ed2f8106423ab9c"},
{file = "viztracer-0.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:a539ed578e4462d0107421197c5fb7345a3572560fb940962418e56b72eaf0ac"},
{file = "viztracer-0.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a36db9b610131ce52ad911a08075e73ece8f863a74cafa846266e73bf49d4fae"},
{file = "viztracer-0.17.0-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:fb64f867d36c2fff411b5f155e3f6d383bee76e4c927c9df321012ab34e05afe"},
{file = "viztracer-0.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b4cb83b86aebd24f872c4baead1c41ec114ca5f104c7297a8852bd37a1d3fa8"},
{file = "viztracer-0.17.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc4de3e6d5a7906472a777ebc1908bd4f7f38b8d509b9fd725e8a568e13365f8"},
{file = "viztracer-0.17.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcac5ae4d20980883312e9527af6bfb87fdc5cdeafe7a67caf4e060f9066f5ed"},
{file = "viztracer-0.17.0-cp313-cp313-win32.whl", hash = "sha256:3e9bf5674da8476027f4c94f33673136bf24d639260b7f5adcd3bcba1987ede7"},
{file = "viztracer-0.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:2dce28aed18faa8d923e59b76e13c18fe79fa7e9c5dbd632fcbaeae890c6c84f"},
{file = "viztracer-0.17.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:227fa597fc837697483b91ccc93b12b3da67c1991b4716bc19096ec1419ed4e6"},
{file = "viztracer-0.17.0-cp313-cp313t-macosx_11_0_x86_64.whl", hash = "sha256:0eb962c2459fb2e781691bfcb4d6cfa1ded90211ee6b1be68e4e31982b9f2f3f"},
{file = "viztracer-0.17.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96373fb17a94b96925c146caa7651ad16de5fd4a10d69cf11d58fb2a417943bd"},
{file = "viztracer-0.17.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b606830ee327e0e02d317a545d88f5a52ca8ad723c15e9e3f0063ac39f5668b1"},
{file = "viztracer-0.17.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:febd0d23c1782461831a8c83df92475ab3ac019b5cf57208272a43eee3bda58b"},
{file = "viztracer-0.17.0-cp313-cp313t-win32.whl", hash = "sha256:cd0b99c36ed0e1237fcabcd51952ef98c52ec2daab7605979874b89dec0cdeee"},
{file = "viztracer-0.17.0-cp313-cp313t-win_amd64.whl", hash = "sha256:45803cf94c8c3ea622221c53df5aa9a9afcb457c8272f7e79bcf6eec56f0eac4"},
{file = "viztracer-0.17.0.tar.gz", hash = "sha256:20397b0c2a6341513596fe4c292994c3db8f3f6b79a0a4f497dadb9d73d988b8"},
]

View File

@ -23,6 +23,8 @@ tag_regex = '^v(?:\D*)?(?P<version>([1-9][0-9]*!)?(0|[1-9][0-9]*)(\.(0|[1-9][0-9
[tool.pdm.dev-dependencies]
dev = [
"pytest>=8.3.3",
"mypy>=1.11.2",
"viztracer>=0.17.0",
]
[tool.pdm]
distribution = true

View File

@ -1,30 +1,33 @@
import time
from magicoca.chan import Chan
from multiprocessing import Process
def p1f(chan: Chan[int]):
for i in range(10):
chan << i
chan << -1
def p2f(chan: Chan[int]):
recv_ans = []
while True:
a = int << chan
print("Recv", a)
recv_ans.append(a)
if a == -1:
break
if recv_ans != list(range(10)) + [-1]:
raise ValueError("Chan Shift Test Failed")
class TestChan:
def test_test(self):
print("Test is running")
def test_chan_shift(self):
"""测试运算符"""
ch = Chan[int]()
def p1f(chan: Chan[int]):
for i in range(10):
time.sleep(1)
chan << i
chan << -1
def p2f(chan: Chan[int]):
recv_ans = []
while True:
a = int << chan
print("Recv", a)
recv_ans.append(a)
if a == -1:
break
if recv_ans != list(range(10)) + [-1]:
raise ValueError("Chan Shift Test Failed")
print("Test Chan Shift")
p1 = Process(target=p1f, args=(ch,))
p2 = Process(target=p2f, args=(ch,))
@ -34,25 +37,9 @@ class TestChan:
p2.join()
def test_chan_sr(self):
"""测试收发"""
ch = Chan[int]()
def p1f(chan: Chan[int]):
for i in range(10):
time.sleep(1)
chan.send(i)
chan.send(-1)
def p2f(chan: Chan[int]):
recv_ans = []
while True:
a = chan.recv()
recv_ans.append(a)
print("Recv2", a)
if a == -1:
break
if recv_ans != list(range(10)) + [-1]:
raise ValueError("Chan SR Test Failed")
print("Test Chan SR")
p1 = Process(target=p1f, args=(ch,))
p2 = Process(target=p2f, args=(ch,))
@ -60,3 +47,21 @@ class TestChan:
p2.start()
p1.join()
p2.join()
def test_connect(self):
"""测试双通道连接"""
chan1 = Chan[int]()
chan2 = Chan[int]()
chan1 + chan2
print("Test Chan Connect")
p1 = Process(target=p1f, args=(chan1,))
p2 = Process(target=p2f, args=(chan2,))
p1.start()
p2.start()
p1.join()
p2.join()

38
tests/test_select.py Normal file
View File

@ -0,0 +1,38 @@
import time
from multiprocessing import Process
from magicoca import Chan, select
def send_process(chan: Chan[int], _id: int):
for i in range(10):
chan << i
time.sleep(0.1 * _id)
def recv_process(chan_list: list[Chan[int]]):
c = []
for t in select(*chan_list):
c.append(t)
print("Select", t)
if len(c) == 30:
break
class TestSelect:
def test_select(self):
ch1 = Chan[int]()
ch2 = Chan[int]()
ch3 = Chan[int]()
p1 = Process(target=send_process, args=(ch1, 1))
p2 = Process(target=send_process, args=(ch2, 2))
p3 = Process(target=send_process, args=(ch3, 3))
p4 = Process(target=recv_process, args=([ch1, ch2, ch3],))
p1.start()
p2.start()
p3.start()
p4.start()
p1.join()
p2.join()
p3.join()
p4.join()