microagent 1.7.2__tar.gz → 1.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- microagent-1.8.0/.github/workflows/python-app.yml +43 -0
- {microagent-1.7.2 → microagent-1.8.0}/CHANGELOG.rst +20 -0
- {microagent-1.7.2 → microagent-1.8.0}/Makefile +1 -1
- {microagent-1.7.2 → microagent-1.8.0}/PKG-INFO +18 -18
- {microagent-1.7.2 → microagent-1.8.0}/README.rst +7 -10
- {microagent-1.7.2 → microagent-1.8.0}/docs/conf.py +2 -2
- {microagent-1.7.2 → microagent-1.8.0}/docs/example_agents.rst +9 -3
- microagent-1.8.0/examples/push_agent.py +59 -0
- {microagent-1.7.2 → microagent-1.8.0}/examples/signals.json +5 -1
- {microagent-1.7.2 → microagent-1.8.0}/microagent/__init__.py +15 -6
- {microagent-1.7.2 → microagent-1.8.0}/microagent/abc.py +0 -2
- {microagent-1.7.2 → microagent-1.8.0}/microagent/agent.py +0 -2
- {microagent-1.7.2 → microagent-1.8.0}/microagent/broker.py +16 -4
- {microagent-1.7.2 → microagent-1.8.0}/microagent/bus.py +2 -3
- {microagent-1.7.2 → microagent-1.8.0}/microagent/hooks.py +0 -2
- {microagent-1.7.2 → microagent-1.8.0}/microagent/launcher.py +0 -2
- {microagent-1.7.2 → microagent-1.8.0}/microagent/queue.py +22 -6
- {microagent-1.7.2 → microagent-1.8.0}/microagent/signal.py +0 -2
- {microagent-1.7.2 → microagent-1.8.0}/microagent/timer.py +1 -3
- {microagent-1.7.2 → microagent-1.8.0}/microagent/tools/amqp.py +35 -28
- {microagent-1.7.2 → microagent-1.8.0}/microagent/tools/kafka.py +0 -1
- {microagent-1.7.2 → microagent-1.8.0}/microagent/tools/mocks.py +2 -1
- {microagent-1.7.2 → microagent-1.8.0}/microagent/tools/redis.py +2 -2
- {microagent-1.7.2 → microagent-1.8.0}/microagent/utils.py +1 -2
- {microagent-1.7.2 → microagent-1.8.0}/microagent.egg-info/PKG-INFO +18 -18
- {microagent-1.7.2 → microagent-1.8.0}/microagent.egg-info/SOURCES.txt +1 -0
- {microagent-1.7.2 → microagent-1.8.0}/microagent.egg-info/requires.txt +6 -6
- {microagent-1.7.2 → microagent-1.8.0}/pyproject.toml +16 -15
- {microagent-1.7.2 → microagent-1.8.0}/tests/stuff.json +3 -1
- {microagent-1.7.2 → microagent-1.8.0}/tests/test_agent.py +2 -2
- {microagent-1.7.2 → microagent-1.8.0}/tests/test_api.py +9 -15
- {microagent-1.7.2 → microagent-1.8.0}/tests/test_broker.py +6 -3
- {microagent-1.7.2 → microagent-1.8.0}/tests/test_bus.py +2 -3
- {microagent-1.7.2 → microagent-1.8.0}/tests/test_launcher.py +0 -1
- {microagent-1.7.2 → microagent-1.8.0}/tests/test_timer.py +2 -3
- {microagent-1.7.2 → microagent-1.8.0}/tests/test_tools_amqp.py +52 -17
- {microagent-1.7.2 → microagent-1.8.0}/tests/test_tools_kafka.py +0 -1
- {microagent-1.7.2 → microagent-1.8.0}/tests/test_tools_redis.py +2 -2
- microagent-1.7.2/.github/workflows/python-app.yml +0 -34
- {microagent-1.7.2 → microagent-1.8.0}/.gitignore +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/.readthedocs.yml +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/.travis.yml +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/LICENSE.md +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/Makefile +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/agent.rst +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/amqp.rst +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/broker.rst +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/bus.rst +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/example_run.rst +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/hooks.rst +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/index.rst +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/kafka.rst +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/launcher.rst +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/ma-bus.dia +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/ma-bus.svg +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/ma-logo.dia +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/ma-logo.svg +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/ma-periodic.dia +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/ma-periodic.svg +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/ma-queue.dia +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/ma-queue.svg +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/mocks.rst +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/periodic.rst +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/redis.rst +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/docs/requirements.txt +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/examples/__init__.py +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/examples/comment_agent.py +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/examples/email_agent.py +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/examples/redis_server.py +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/examples/requirements.txt +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/examples/settings.py +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/examples/user_agent.py +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/microagent/py.typed +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/microagent/tools/__init__.py +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/microagent.egg-info/dependency_links.txt +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/microagent.egg-info/entry_points.txt +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/microagent.egg-info/top_level.txt +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/setup.cfg +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/tests/__init__.py +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/tests/conftest.py +0 -0
- {microagent-1.7.2 → microagent-1.8.0}/tests/test_hook.py +0 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on: [push]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
tests:
|
|
7
|
+
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
strategy:
|
|
10
|
+
matrix:
|
|
11
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v5
|
|
15
|
+
|
|
16
|
+
- name: Install uv
|
|
17
|
+
uses: astral-sh/setup-uv@v6
|
|
18
|
+
with:
|
|
19
|
+
python-version: ${{ matrix.python-version }}
|
|
20
|
+
enable-cache: true
|
|
21
|
+
|
|
22
|
+
- name: Lint with ruff
|
|
23
|
+
run: uvx ruff check microagent
|
|
24
|
+
|
|
25
|
+
- name: Lint with mypy
|
|
26
|
+
run: uvx mypy microagent
|
|
27
|
+
|
|
28
|
+
- name: Testing
|
|
29
|
+
run: uvx --with .[dev] pytest tests
|
|
30
|
+
|
|
31
|
+
check:
|
|
32
|
+
if: always()
|
|
33
|
+
|
|
34
|
+
needs:
|
|
35
|
+
- tests
|
|
36
|
+
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
|
|
39
|
+
steps:
|
|
40
|
+
- name: Decide whether the needed jobs succeeded or failed
|
|
41
|
+
uses: re-actors/alls-green@release/v1
|
|
42
|
+
with:
|
|
43
|
+
jobs: ${{ toJSON(needs) }}
|
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
Changelog
|
|
3
3
|
=========
|
|
4
4
|
|
|
5
|
+
|
|
6
|
+
1.8.0 (2026-02-26)
|
|
7
|
+
==================
|
|
8
|
+
|
|
9
|
+
- The broker now supports the use of exchanges and topic-based routing
|
|
10
|
+
- Drop support python 3.9, add python 3.13, 3.14
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
1.7.3 (2024-03-12)
|
|
14
|
+
================
|
|
15
|
+
|
|
16
|
+
- RPC bugfix
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
1.7.2 (2024-03-05)
|
|
20
|
+
================
|
|
21
|
+
|
|
22
|
+
- Typing fixes
|
|
23
|
+
|
|
24
|
+
|
|
5
25
|
1.7 (2023-10-01)
|
|
6
26
|
================
|
|
7
27
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: microagent
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.0
|
|
4
4
|
Summary: Tool for agent ecosystem
|
|
5
5
|
Author-email: Dmitriy Vlasov <scailer@yandex.ru>
|
|
6
6
|
License: MIT License
|
|
@@ -39,17 +39,19 @@ Classifier: Programming Language :: Python :: 3
|
|
|
39
39
|
Classifier: Programming Language :: Python :: 3.10
|
|
40
40
|
Classifier: Programming Language :: Python :: 3.11
|
|
41
41
|
Classifier: Programming Language :: Python :: 3.12
|
|
42
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
43
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
42
44
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
43
45
|
Classifier: Framework :: AsyncIO
|
|
44
46
|
Requires-Python: >=3.10
|
|
45
47
|
Description-Content-Type: text/x-rst
|
|
46
48
|
License-File: LICENSE.md
|
|
47
49
|
Provides-Extra: redis
|
|
48
|
-
Requires-Dist: redis[hiredis]
|
|
50
|
+
Requires-Dist: redis[hiredis]>5; extra == "redis"
|
|
49
51
|
Provides-Extra: amqp
|
|
50
|
-
Requires-Dist: aiormq==6.
|
|
52
|
+
Requires-Dist: aiormq==6.9.*; extra == "amqp"
|
|
51
53
|
Provides-Extra: kafka
|
|
52
|
-
Requires-Dist: aiokafka==0.
|
|
54
|
+
Requires-Dist: aiokafka==0.13.*; extra == "kafka"
|
|
53
55
|
Provides-Extra: dev
|
|
54
56
|
Requires-Dist: pytest; extra == "dev"
|
|
55
57
|
Requires-Dist: pytest-runner; extra == "dev"
|
|
@@ -58,9 +60,10 @@ Requires-Dist: pytest-cov; extra == "dev"
|
|
|
58
60
|
Requires-Dist: coverage; extra == "dev"
|
|
59
61
|
Requires-Dist: mypy; extra == "dev"
|
|
60
62
|
Requires-Dist: ruff; extra == "dev"
|
|
61
|
-
Requires-Dist: redis
|
|
62
|
-
Requires-Dist: aiormq==6.
|
|
63
|
-
Requires-Dist: aiokafka==0.
|
|
63
|
+
Requires-Dist: redis[hiredis]>5; extra == "dev"
|
|
64
|
+
Requires-Dist: aiormq==6.9.*; extra == "dev"
|
|
65
|
+
Requires-Dist: aiokafka==0.13.*; extra == "dev"
|
|
66
|
+
Dynamic: license-file
|
|
64
67
|
|
|
65
68
|
MicroAgent
|
|
66
69
|
==========
|
|
@@ -77,18 +80,15 @@ MicroAgent
|
|
|
77
80
|
.. image:: https://img.shields.io/pypi/status/microagent.svg
|
|
78
81
|
:target: https://pypi.python.org/pypi/microagent
|
|
79
82
|
|
|
80
|
-
.. image:: https://img.shields.io/pypi/
|
|
83
|
+
.. image:: https://img.shields.io/pypi/dm/microagent.svg
|
|
81
84
|
:target: https://pypi.python.org/pypi/microagent
|
|
82
85
|
|
|
83
|
-
.. image:: https://codecov.io/gh/scailer/microagent/branch/master/graph/badge.svg
|
|
84
|
-
:target: https://codecov.io/gh/scailer/microagent
|
|
85
|
-
|
|
86
|
-
.. image:: https://travis-ci.com/scailer/microagent.svg?branch=master
|
|
87
|
-
:target: https://travis-ci.con/scailer/microagent
|
|
88
|
-
|
|
89
86
|
.. image:: https://readthedocs.org/projects/microagent/badge/?version=latest&style=flat
|
|
90
87
|
:target: https://microagent.readthedocs.io/
|
|
91
88
|
|
|
89
|
+
.. image:: https://github.com/scailer/microagent/workflows/Tests/badge.svg
|
|
90
|
+
:target: https://github.com/scailer/microagent/actions
|
|
91
|
+
|
|
92
92
|
|
|
93
93
|
The goal of this project is to facilitate the creation of **microservices**
|
|
94
94
|
interacting via a **signal bus** and/or **queue broker**.
|
|
@@ -107,7 +107,7 @@ Tool provide features:
|
|
|
107
107
|
|
|
108
108
|
* running a **periodical tasks** (interval or as CRON)
|
|
109
109
|
* specification of signals (events), their sending and receiving via the bus (redis_)
|
|
110
|
-
* description of queues, sending and receiving messages via the queue broker (
|
|
110
|
+
* description of queues, sending and receiving messages via the queue broker (aiormq_, kafka_, redis_)
|
|
111
111
|
* limited **RPC** via signal bus
|
|
112
112
|
* launching sub-services (in the same process)
|
|
113
113
|
* launching a group of microagents (each in a separate process)
|
|
@@ -164,7 +164,7 @@ With redis_ backend provide signal bus and list-based queues::
|
|
|
164
164
|
|
|
165
165
|
pip install 'microagent[redis]'
|
|
166
166
|
|
|
167
|
-
With
|
|
167
|
+
With aiormq_ backend provide queues over AMQP (RabbitMQ)::
|
|
168
168
|
|
|
169
169
|
pip install 'microagent[amqp]'
|
|
170
170
|
|
|
@@ -174,6 +174,6 @@ With kafka_ backend provide queues over Kafka (experemental)::
|
|
|
174
174
|
|
|
175
175
|
|
|
176
176
|
.. _redis: https://pypi.org/project/redis/
|
|
177
|
-
..
|
|
177
|
+
.. _aiormq: https://pypi.org/project/aiormq/
|
|
178
178
|
.. _kafka: https://pypi.org/project/aiokafka/
|
|
179
179
|
.. _documentation: https://microagent.readthedocs.io/
|
|
@@ -13,18 +13,15 @@ MicroAgent
|
|
|
13
13
|
.. image:: https://img.shields.io/pypi/status/microagent.svg
|
|
14
14
|
:target: https://pypi.python.org/pypi/microagent
|
|
15
15
|
|
|
16
|
-
.. image:: https://img.shields.io/pypi/
|
|
16
|
+
.. image:: https://img.shields.io/pypi/dm/microagent.svg
|
|
17
17
|
:target: https://pypi.python.org/pypi/microagent
|
|
18
18
|
|
|
19
|
-
.. image:: https://codecov.io/gh/scailer/microagent/branch/master/graph/badge.svg
|
|
20
|
-
:target: https://codecov.io/gh/scailer/microagent
|
|
21
|
-
|
|
22
|
-
.. image:: https://travis-ci.com/scailer/microagent.svg?branch=master
|
|
23
|
-
:target: https://travis-ci.con/scailer/microagent
|
|
24
|
-
|
|
25
19
|
.. image:: https://readthedocs.org/projects/microagent/badge/?version=latest&style=flat
|
|
26
20
|
:target: https://microagent.readthedocs.io/
|
|
27
21
|
|
|
22
|
+
.. image:: https://github.com/scailer/microagent/workflows/Tests/badge.svg
|
|
23
|
+
:target: https://github.com/scailer/microagent/actions
|
|
24
|
+
|
|
28
25
|
|
|
29
26
|
The goal of this project is to facilitate the creation of **microservices**
|
|
30
27
|
interacting via a **signal bus** and/or **queue broker**.
|
|
@@ -43,7 +40,7 @@ Tool provide features:
|
|
|
43
40
|
|
|
44
41
|
* running a **periodical tasks** (interval or as CRON)
|
|
45
42
|
* specification of signals (events), their sending and receiving via the bus (redis_)
|
|
46
|
-
* description of queues, sending and receiving messages via the queue broker (
|
|
43
|
+
* description of queues, sending and receiving messages via the queue broker (aiormq_, kafka_, redis_)
|
|
47
44
|
* limited **RPC** via signal bus
|
|
48
45
|
* launching sub-services (in the same process)
|
|
49
46
|
* launching a group of microagents (each in a separate process)
|
|
@@ -100,7 +97,7 @@ With redis_ backend provide signal bus and list-based queues::
|
|
|
100
97
|
|
|
101
98
|
pip install 'microagent[redis]'
|
|
102
99
|
|
|
103
|
-
With
|
|
100
|
+
With aiormq_ backend provide queues over AMQP (RabbitMQ)::
|
|
104
101
|
|
|
105
102
|
pip install 'microagent[amqp]'
|
|
106
103
|
|
|
@@ -110,6 +107,6 @@ With kafka_ backend provide queues over Kafka (experemental)::
|
|
|
110
107
|
|
|
111
108
|
|
|
112
109
|
.. _redis: https://pypi.org/project/redis/
|
|
113
|
-
..
|
|
110
|
+
.. _aiormq: https://pypi.org/project/aiormq/
|
|
114
111
|
.. _kafka: https://pypi.org/project/aiokafka/
|
|
115
112
|
.. _documentation: https://microagent.readthedocs.io/
|
|
@@ -220,7 +220,7 @@ epub_exclude_files = ['search.html']
|
|
|
220
220
|
# -- Options for intersphinx extension ---------------------------------------
|
|
221
221
|
|
|
222
222
|
# Example configuration for intersphinx: refer to the Python standard library.
|
|
223
|
-
intersphinx_mapping = {'https://docs.python.org/': None}
|
|
223
|
+
# intersphinx_mapping = {'https://docs.python.org/': None}
|
|
224
224
|
|
|
225
225
|
# -- Options for todo extension ----------------------------------------------
|
|
226
226
|
|
|
@@ -229,6 +229,6 @@ todo_include_todos = True
|
|
|
229
229
|
|
|
230
230
|
extlinks = {
|
|
231
231
|
'redis': ('https://redis.readthedocs.io/%s', ''),
|
|
232
|
-
'
|
|
232
|
+
'aiormq': ('https://github.com/mosquito/aiormq/blob/master/README.rst%s', ''),
|
|
233
233
|
'kafka': ('https://aiokafka.readthedocs.io/%s', ''),
|
|
234
234
|
}
|
|
@@ -4,6 +4,12 @@ Agent examples
|
|
|
4
4
|
======================================
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
signals.json
|
|
8
|
+
|
|
9
|
+
.. include:: ../examples/signals.json
|
|
10
|
+
:code: json
|
|
11
|
+
|
|
12
|
+
|
|
7
13
|
user_agent.py
|
|
8
14
|
|
|
9
15
|
.. include:: ../examples/user_agent.py
|
|
@@ -22,7 +28,7 @@ email_agent.py
|
|
|
22
28
|
:code: python
|
|
23
29
|
|
|
24
30
|
|
|
25
|
-
|
|
31
|
+
push_agent.py
|
|
26
32
|
|
|
27
|
-
.. include:: ../examples/
|
|
28
|
-
:code:
|
|
33
|
+
.. include:: ../examples/push_agent.py
|
|
34
|
+
:code: python
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# mypy: ignore-errors
|
|
2
|
+
import asyncio
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from microagent import MicroAgent, consumer, load_stuff, on, periodic
|
|
8
|
+
from microagent.tools import amqp, redis
|
|
9
|
+
|
|
10
|
+
logging.basicConfig(format=(
|
|
11
|
+
'%(levelname)-8s [pid#%(process)d] %(asctime)s %(name)s '
|
|
12
|
+
'%(filename)s:%(lineno)d %(message)s'
|
|
13
|
+
), stream=sys.stdout, level=logging.INFO)
|
|
14
|
+
|
|
15
|
+
cur_dir = os.path.dirname(os.path.realpath(__file__))
|
|
16
|
+
signals, queues = load_stuff('file://' + os.path.join(cur_dir, 'signals.json'))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class UserAgent(MicroAgent):
|
|
20
|
+
@on('pre_start')
|
|
21
|
+
def setup(self):
|
|
22
|
+
self.log.info('Run ...\n %s', self.info())
|
|
23
|
+
|
|
24
|
+
@periodic(period=15, timeout=10, start_after=3)
|
|
25
|
+
async def example_periodic_task_send_push(self):
|
|
26
|
+
self.log.info('Run periodic task')
|
|
27
|
+
await self.broker.push3.send({'text': 'informer text'}, topic='msg.ios')
|
|
28
|
+
await self.broker.push.send({'text': 'informer 1'})
|
|
29
|
+
|
|
30
|
+
@consumer(queues.android)
|
|
31
|
+
async def example_read_queue_android(self, **kwargs):
|
|
32
|
+
self.log.info('Catch android %s', kwargs)
|
|
33
|
+
|
|
34
|
+
@consumer(queues.ios)
|
|
35
|
+
async def example_read_queue_ios(self, **kwargs):
|
|
36
|
+
self.log.info('Catch ios %s', kwargs)
|
|
37
|
+
|
|
38
|
+
@consumer(queues.android_a)
|
|
39
|
+
async def example_read_queue_android_a(self, **kwargs):
|
|
40
|
+
self.log.info('Catch android_a %s', kwargs)
|
|
41
|
+
|
|
42
|
+
@consumer(queues.ios_a)
|
|
43
|
+
async def example_read_queue_ios_a(self, **kwargs):
|
|
44
|
+
self.log.info('Catch ios_a %s', kwargs)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
async def main():
|
|
48
|
+
bus = redis.RedisSignalBus('redis://localhost/7')
|
|
49
|
+
broker = amqp.AMQPBroker('amqp://guest:guest@localhost:5672/')
|
|
50
|
+
await broker.push3.send({'q':1}, topic='msg.android')
|
|
51
|
+
|
|
52
|
+
agent = UserAgent(bus=bus, broker=broker)
|
|
53
|
+
await agent.start()
|
|
54
|
+
|
|
55
|
+
while True:
|
|
56
|
+
await asyncio.sleep(1)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
asyncio.run(main())
|
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
}}
|
|
12
12
|
],
|
|
13
13
|
"queues": [
|
|
14
|
-
{"name": "mailer"}
|
|
14
|
+
{"name": "mailer"},
|
|
15
|
+
{"name": "android", "exchange": "push3", "topics": ["*.android"]},
|
|
16
|
+
{"name": "ios", "exchange": "push3", "topics": ["*.ios"]},
|
|
17
|
+
{"name": "android_a", "exchange": "push"},
|
|
18
|
+
{"name": "ios_a", "exchange": "push"}
|
|
15
19
|
]
|
|
16
20
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
__version__ = '1.
|
|
1
|
+
__version__ = '1.8.0'
|
|
2
2
|
|
|
3
3
|
import importlib
|
|
4
4
|
import json
|
|
5
5
|
import urllib.request
|
|
6
|
-
|
|
7
6
|
from collections import abc
|
|
8
7
|
from typing import Any, NamedTuple
|
|
9
8
|
|
|
@@ -16,7 +15,6 @@ from .signal import Receiver, ReceiverArgs, Signal
|
|
|
16
15
|
from .timer import CRONArgs, CRONTask, PeriodicArgs, PeriodicTask, cron_parser
|
|
17
16
|
from .utils import make_bound_key
|
|
18
17
|
|
|
19
|
-
|
|
20
18
|
__all__ = ['Signal', 'Queue', 'MicroAgent', 'ServerInterrupt', 'receiver', 'consumer',
|
|
21
19
|
'periodic', 'cron', 'on', 'load_stuff', 'load_signals', 'load_queues']
|
|
22
20
|
|
|
@@ -58,10 +56,18 @@ def load_stuff(source: str) -> tuple[Any, Any]:
|
|
|
58
56
|
type_map = {name: get_types(_type) for name, _type in providing_args.items()}
|
|
59
57
|
providing_args = list(providing_args)
|
|
60
58
|
|
|
61
|
-
Signal(
|
|
59
|
+
Signal(
|
|
60
|
+
name=_data['name'],
|
|
61
|
+
providing_args=providing_args,
|
|
62
|
+
type_map=type_map
|
|
63
|
+
)
|
|
62
64
|
|
|
63
65
|
for _data in data.get('queues', []):
|
|
64
|
-
Queue(
|
|
66
|
+
Queue(
|
|
67
|
+
name=_data['name'],
|
|
68
|
+
exchange=_data.get('exchange', ''),
|
|
69
|
+
topics=_data.get('topics', [])
|
|
70
|
+
)
|
|
65
71
|
|
|
66
72
|
if data.get('jsonlib'):
|
|
67
73
|
jsonlib = importlib.import_module(data['jsonlib']) # type: ignore
|
|
@@ -129,7 +135,10 @@ def load_queues(source: str) -> NamedTuple:
|
|
|
129
135
|
{
|
|
130
136
|
"queues": [
|
|
131
137
|
{"name": "mailer"},
|
|
132
|
-
{"name": "
|
|
138
|
+
{"name": "pusher1", "exchange": "push"},
|
|
139
|
+
{"name": "pusher2", "exchange": "push"},
|
|
140
|
+
{"name": "create_event", "exchange": "logs", "topics": ["*created"]},
|
|
141
|
+
{"name": "update_event", "exchange": "logs", "topics": ["*updated"]},
|
|
133
142
|
]
|
|
134
143
|
}
|
|
135
144
|
'''
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
|
|
3
2
|
from collections.abc import Awaitable, Callable
|
|
4
3
|
from typing import Any, Literal, Protocol
|
|
5
4
|
|
|
6
|
-
|
|
7
5
|
HookLabel = Literal['server', 'pre_start', 'post_start', 'pre_stop']
|
|
8
6
|
HookFunc = Callable[[Any], Awaitable[None]]
|
|
9
7
|
PeriodicFunc = Callable[[Any], Awaitable[None]]
|
|
@@ -77,7 +77,6 @@ Using MicroAgent resources.
|
|
|
77
77
|
|
|
78
78
|
import asyncio
|
|
79
79
|
import logging
|
|
80
|
-
|
|
81
80
|
from collections.abc import Callable, Iterable
|
|
82
81
|
from dataclasses import dataclass, field
|
|
83
82
|
from datetime import datetime, timedelta, timezone
|
|
@@ -90,7 +89,6 @@ from .queue import Consumer
|
|
|
90
89
|
from .signal import Receiver
|
|
91
90
|
from .timer import CRONTask, PeriodicTask
|
|
92
91
|
|
|
93
|
-
|
|
94
92
|
HandlerTypes = TypeVar('HandlerTypes', Receiver, Consumer, PeriodicTask, CRONTask, Hook)
|
|
95
93
|
|
|
96
94
|
|
|
@@ -7,10 +7,17 @@ The broker performs the functions of guaranteed and consistent transmission of m
|
|
|
7
7
|
from the product to the consumer, many to one (or according to the broker's own logic).
|
|
8
8
|
The message has a free structure, fully defined in the user area.
|
|
9
9
|
|
|
10
|
+
The broker supports three types of architecture:
|
|
11
|
+
|
|
12
|
+
- **Simple:** messages are delivered to a single queue.
|
|
13
|
+
- **Branching:** a message is sent to an exchange, from which it is copied to multiple queues.
|
|
14
|
+
- **Topics:** a message is sent to an exchange and, based on the topic, \
|
|
15
|
+
is routed to queues subscribed to that topic.
|
|
16
|
+
|
|
10
17
|
Implementations:
|
|
11
18
|
|
|
12
19
|
* :ref:`redis <tools-redis>`
|
|
13
|
-
* :ref:`
|
|
20
|
+
* :ref:`aiormq <tools-amqp>`
|
|
14
21
|
* :ref:`kafka <tools-kafka>`
|
|
15
22
|
|
|
16
23
|
|
|
@@ -22,9 +29,13 @@ Using QueueBroker separately (sending only)
|
|
|
22
29
|
from microagent.tools.redis import RedisBroker
|
|
23
30
|
|
|
24
31
|
queues = load_queues('file://queues.json')
|
|
25
|
-
|
|
26
32
|
broker = RedisBroker('redis://localhost/7')
|
|
27
|
-
|
|
33
|
+
|
|
34
|
+
await broker.user_created.send({'user_id': 1}) # send to queue
|
|
35
|
+
|
|
36
|
+
await broker.push.send({'user_id': 1, 'text': 'hi!'}) # send to exchange
|
|
37
|
+
|
|
38
|
+
await broker.logs.send({'text': 'success'}, topic='user.message') # send with topic
|
|
28
39
|
|
|
29
40
|
|
|
30
41
|
Using with MicroAgent
|
|
@@ -47,7 +58,6 @@ Using with MicroAgent
|
|
|
47
58
|
'''
|
|
48
59
|
import logging
|
|
49
60
|
import uuid
|
|
50
|
-
|
|
51
61
|
from abc import abstractmethod
|
|
52
62
|
from dataclasses import dataclass, field
|
|
53
63
|
from typing import Any
|
|
@@ -160,6 +170,8 @@ class BoundQueue(QueueProtocol):
|
|
|
160
170
|
queue: Queue
|
|
161
171
|
|
|
162
172
|
async def send(self, message: dict, **options: Any) -> None:
|
|
173
|
+
if 'exchange' not in options and self.queue.exchange:
|
|
174
|
+
options['exchange'] = self.queue.exchange
|
|
163
175
|
await self.broker.send(self.queue.name, self.queue.serialize(message), **options)
|
|
164
176
|
|
|
165
177
|
async def length(self) -> int:
|
|
@@ -49,7 +49,6 @@ import contextlib
|
|
|
49
49
|
import logging
|
|
50
50
|
import time
|
|
51
51
|
import uuid
|
|
52
|
-
|
|
53
52
|
from abc import abstractmethod
|
|
54
53
|
from collections import abc, defaultdict
|
|
55
54
|
from dataclasses import dataclass, field
|
|
@@ -333,6 +332,6 @@ def check_types(signal: Signal, data: dict, log: logging.Logger) -> None:
|
|
|
333
332
|
if signal.type_map:
|
|
334
333
|
for key, value in data.items():
|
|
335
334
|
if key not in signal.type_map:
|
|
336
|
-
log.warning('Receiver get unknown arg "%s" %s', key, value)
|
|
335
|
+
log.warning('Receiver "%s" get unknown arg "%s" %s', signal.name, key, value)
|
|
337
336
|
elif not isinstance(value, signal.type_map[key]):
|
|
338
|
-
log.warning('Receiver get wrong type for "%s" %s', key, value)
|
|
337
|
+
log.warning('Receiver "%s" get wrong type for "%s" %s', signal.name, key, value)
|
|
@@ -24,14 +24,12 @@ allow provide endpoints for microagent, such as http, websocket, smtp or other.
|
|
|
24
24
|
'''
|
|
25
25
|
|
|
26
26
|
import inspect
|
|
27
|
-
|
|
28
27
|
from collections import abc, defaultdict
|
|
29
28
|
from dataclasses import dataclass
|
|
30
29
|
from typing import TYPE_CHECKING, ClassVar, TypedDict
|
|
31
30
|
|
|
32
31
|
from .abc import BoundKey, HookFunc
|
|
33
32
|
|
|
34
|
-
|
|
35
33
|
if TYPE_CHECKING:
|
|
36
34
|
from .agent import MicroAgent
|
|
37
35
|
|
|
@@ -23,13 +23,11 @@ import multiprocessing
|
|
|
23
23
|
import os
|
|
24
24
|
import signal
|
|
25
25
|
import time
|
|
26
|
-
|
|
27
26
|
from collections.abc import Iterator
|
|
28
27
|
from functools import partial
|
|
29
28
|
from itertools import chain
|
|
30
29
|
from typing import TYPE_CHECKING, Any
|
|
31
30
|
|
|
32
|
-
|
|
33
31
|
if TYPE_CHECKING:
|
|
34
32
|
from .agent import MicroAgent
|
|
35
33
|
from .broker import AbstractQueueBroker
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import json
|
|
2
|
-
|
|
3
|
-
from dataclasses import dataclass
|
|
2
|
+
from dataclasses import dataclass, field
|
|
4
3
|
from types import ModuleType
|
|
5
4
|
from typing import TYPE_CHECKING, ClassVar, TypedDict
|
|
6
5
|
|
|
7
6
|
from .abc import BoundKey, ConsumerFunc
|
|
8
7
|
|
|
9
|
-
|
|
10
8
|
if TYPE_CHECKING:
|
|
11
9
|
from .agent import MicroAgent
|
|
12
10
|
|
|
@@ -41,8 +39,10 @@ class Queue:
|
|
|
41
39
|
|
|
42
40
|
{
|
|
43
41
|
"queues": [
|
|
44
|
-
{"name": "mailer"},
|
|
45
|
-
{"name": "pusher"},
|
|
42
|
+
{"name": "mailer"}, // simple
|
|
43
|
+
{"name": "pusher", "exchange": "events"}, // fanout
|
|
44
|
+
{"name": "logger", "exchange": "events"},
|
|
45
|
+
{"name": "save", "exchange": "msg", "topics": "m.*"}, // topics
|
|
46
46
|
]
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -53,14 +53,30 @@ class Queue:
|
|
|
53
53
|
some_queue = Queue(
|
|
54
54
|
name='some_queue'
|
|
55
55
|
)
|
|
56
|
+
|
|
57
|
+
else_queue = Queue(
|
|
58
|
+
name='else_queue',
|
|
59
|
+
exchange='fanout_exchange'
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
topic_queue = Queue(
|
|
63
|
+
name='topic_queue',
|
|
64
|
+
exchange='topic_exchange',
|
|
65
|
+
topics=['message.*']
|
|
66
|
+
)
|
|
56
67
|
'''
|
|
57
68
|
name: str
|
|
69
|
+
exchange: str = ''
|
|
70
|
+
topics: list[str] = field(default_factory=list)
|
|
58
71
|
|
|
59
72
|
_queues: ClassVar[dict[str, 'Queue']] = {}
|
|
73
|
+
_exchanges: ClassVar[dict[str, 'Queue']] = {}
|
|
60
74
|
_jsonlib: ClassVar[ModuleType] = json
|
|
61
75
|
|
|
62
76
|
def __post_init__(self) -> None:
|
|
63
77
|
self._queues[self.name] = self
|
|
78
|
+
if self.exchange:
|
|
79
|
+
self._exchanges[self.exchange] = self
|
|
64
80
|
|
|
65
81
|
def __repr__(self) -> str:
|
|
66
82
|
return f'<Queue {self.name}>'
|
|
@@ -81,7 +97,7 @@ class Queue:
|
|
|
81
97
|
def get(cls, name: str) -> 'Queue':
|
|
82
98
|
''' Get the queue instance by name '''
|
|
83
99
|
try:
|
|
84
|
-
return cls._queues[name]
|
|
100
|
+
return cls._exchanges.get(name) or cls._queues[name]
|
|
85
101
|
except KeyError as exc:
|
|
86
102
|
raise QueueNotFound(f'No such queue {name}') from exc
|
|
87
103
|
|
|
@@ -23,7 +23,6 @@ import asyncio
|
|
|
23
23
|
import inspect
|
|
24
24
|
import re
|
|
25
25
|
import time
|
|
26
|
-
|
|
27
26
|
from collections.abc import Callable
|
|
28
27
|
from dataclasses import dataclass
|
|
29
28
|
from datetime import datetime, timedelta, timezone
|
|
@@ -31,7 +30,6 @@ from typing import TYPE_CHECKING, ClassVar, NamedTuple, TypedDict
|
|
|
31
30
|
|
|
32
31
|
from .abc import BoundKey, PeriodicFunc
|
|
33
32
|
|
|
34
|
-
|
|
35
33
|
if TYPE_CHECKING:
|
|
36
34
|
from .agent import MicroAgent
|
|
37
35
|
|
|
@@ -75,7 +73,7 @@ class PeriodicMixin:
|
|
|
75
73
|
self.agent.log.exception(f'Periodic Exception: {exc}')
|
|
76
74
|
|
|
77
75
|
def start(self, start_after: float) -> None:
|
|
78
|
-
asyncio.get_running_loop().call_later(start_after, _periodic, self)
|
|
76
|
+
asyncio.get_running_loop().call_later(start_after, _periodic, self) # type: ignore[arg-type]
|
|
79
77
|
|
|
80
78
|
|
|
81
79
|
class PeriodicArgs(TypedDict):
|