dsmq 0.2.0__py3-none-any.whl → 0.4.0__py3-none-any.whl
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.
- dsmq/demo_linux.py +1 -1
- dsmq/dsmq.py +42 -6
- dsmq/example_get_client.py +6 -13
- dsmq/example_put_client.py +8 -10
- dsmq/example_server.py +2 -2
- dsmq-0.4.0.dist-info/METADATA +146 -0
- dsmq-0.4.0.dist-info/RECORD +10 -0
- {dsmq-0.2.0.dist-info → dsmq-0.4.0.dist-info}/WHEEL +1 -1
- dsmq/py.typed +0 -0
- dsmq-0.2.0.dist-info/METADATA +0 -156
- dsmq-0.2.0.dist-info/RECORD +0 -11
- {dsmq-0.2.0.dist-info → dsmq-0.4.0.dist-info}/licenses/LICENSE +0 -0
dsmq/demo_linux.py
CHANGED
@@ -11,7 +11,7 @@ PORT = 25252
|
|
11
11
|
|
12
12
|
|
13
13
|
def test_server_with_clients():
|
14
|
-
p_server = mp.Process(target=dsmq.
|
14
|
+
p_server = mp.Process(target=dsmq.start_server, args=(HOST, PORT))
|
15
15
|
p_server.start()
|
16
16
|
|
17
17
|
p_putter = mp.Process(target=example_put_client.run, args=(HOST, PORT, 20))
|
dsmq/dsmq.py
CHANGED
@@ -5,11 +5,19 @@ import sys
|
|
5
5
|
from threading import Thread
|
6
6
|
import time
|
7
7
|
|
8
|
+
DEFAULT_HOST = "127.0.0.1"
|
9
|
+
DEFAULT_PORT = 30008
|
10
|
+
|
8
11
|
N_RETRIES = 5
|
9
12
|
FIRST_RETRY = 0.01 # seconds
|
10
13
|
|
11
14
|
|
12
|
-
def
|
15
|
+
def start_server(host=DEFAULT_HOST, port=DEFAULT_PORT):
|
16
|
+
"""
|
17
|
+
For best results, start this running in its own process and walk away.
|
18
|
+
|
19
|
+
|
20
|
+
"""
|
13
21
|
sqlite_conn = sqlite3.connect("file:mem1?mode=memory&cache=shared")
|
14
22
|
cursor = sqlite_conn.cursor()
|
15
23
|
|
@@ -34,12 +42,40 @@ def run(host="127.0.0.1", port=30008):
|
|
34
42
|
while True:
|
35
43
|
socket_conn, addr = s.accept()
|
36
44
|
print(f"Connected by {addr}")
|
37
|
-
Thread(target=
|
45
|
+
Thread(target=_handle_client_connection, args=(socket_conn,)).start()
|
38
46
|
|
39
47
|
sqlite_conn.close()
|
40
48
|
|
41
49
|
|
42
|
-
def
|
50
|
+
def connect_to_server(host=DEFAULT_HOST, port=DEFAULT_PORT):
|
51
|
+
return DSMQClientSideConnection(host, port)
|
52
|
+
|
53
|
+
|
54
|
+
class DSMQClientSideConnection:
|
55
|
+
def __init__(self, host, port):
|
56
|
+
self.dsmq_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
57
|
+
self.dsmq_conn.connect((host, port))
|
58
|
+
|
59
|
+
def get(self, topic):
|
60
|
+
msg = json.dumps({"action": "get", "topic": topic})
|
61
|
+
self.dsmq_conn.sendall(bytes(msg, "utf-8"))
|
62
|
+
|
63
|
+
data = self.dsmq_conn.recv(1024)
|
64
|
+
if not data:
|
65
|
+
raise RuntimeError("Connection terminated by server")
|
66
|
+
msg_str = data.decode("utf-8")
|
67
|
+
msg = json.loads(msg_str)
|
68
|
+
try:
|
69
|
+
return msg["message"]
|
70
|
+
except KeyError:
|
71
|
+
return ""
|
72
|
+
|
73
|
+
def put(self, topic, msg):
|
74
|
+
msg = json.dumps({"action": "put", "topic": topic, "message": msg})
|
75
|
+
self.dsmq_conn.sendall(bytes(msg, "utf-8"))
|
76
|
+
|
77
|
+
|
78
|
+
def _handle_client_connection(socket_conn):
|
43
79
|
sqlite_conn = sqlite3.connect("file:mem1?mode=memory&cache=shared")
|
44
80
|
cursor = sqlite_conn.cursor()
|
45
81
|
|
@@ -140,12 +176,12 @@ if __name__ == "__main__":
|
|
140
176
|
if len(sys.argv) == 3:
|
141
177
|
host = sys.argv[1]
|
142
178
|
port = int(sys.argv[2])
|
143
|
-
|
179
|
+
start_server(host=host, port=port)
|
144
180
|
elif len(sys.argv) == 2:
|
145
181
|
host = sys.argv[1]
|
146
|
-
|
182
|
+
start_server(host=host)
|
147
183
|
elif len(sys.argv) == 1:
|
148
|
-
|
184
|
+
start_server()
|
149
185
|
else:
|
150
186
|
print(
|
151
187
|
"""
|
dsmq/example_get_client.py
CHANGED
@@ -1,21 +1,14 @@
|
|
1
|
-
import json
|
2
|
-
import socket
|
3
1
|
import time
|
2
|
+
import dsmq
|
4
3
|
|
5
4
|
|
6
5
|
def run(host="127.0.0.1", port=30008, n_iter=1000):
|
7
|
-
|
8
|
-
s.connect((host, port))
|
6
|
+
mq = dsmq.connect_to_server(host=host, port=port)
|
9
7
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
data = s.recv(1024)
|
15
|
-
if not data:
|
16
|
-
raise RuntimeError("Connection terminated by server")
|
17
|
-
msg_str = data.decode("utf-8")
|
18
|
-
print(f"client received {json.loads(msg_str)}")
|
8
|
+
for i in range(n_iter):
|
9
|
+
time.sleep(1)
|
10
|
+
msg = mq.get("greetings")
|
11
|
+
print(f"client received {msg}")
|
19
12
|
|
20
13
|
|
21
14
|
if __name__ == "__main__":
|
dsmq/example_put_client.py
CHANGED
@@ -1,18 +1,16 @@
|
|
1
|
-
import json
|
2
|
-
import socket
|
3
1
|
import time
|
2
|
+
import dsmq
|
4
3
|
|
5
4
|
|
6
5
|
def run(host="127.0.0.1", port=30008, n_iter=1000):
|
7
|
-
|
8
|
-
s.connect((host, port))
|
6
|
+
mq = dsmq.connect_to_server(host=host, port=port)
|
9
7
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
8
|
+
for i in range(n_iter):
|
9
|
+
time.sleep(1)
|
10
|
+
msg = f"{i}. Hello, world"
|
11
|
+
topic = "greetings"
|
12
|
+
mq.put(topic, msg)
|
13
|
+
print(f"client sent {msg}")
|
16
14
|
|
17
15
|
|
18
16
|
if __name__ == "__main__":
|
dsmq/example_server.py
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
1
|
+
import dsmq
|
2
2
|
|
3
|
-
dsmq.
|
3
|
+
dsmq.start_server(host="127.0.0.1", port=30008)
|
@@ -0,0 +1,146 @@
|
|
1
|
+
Metadata-Version: 2.3
|
2
|
+
Name: dsmq
|
3
|
+
Version: 0.4.0
|
4
|
+
Summary: A dead simple message queue
|
5
|
+
Requires-Python: >=3.10
|
6
|
+
Description-Content-Type: text/markdown
|
7
|
+
|
8
|
+
# Dead Simple Message Queue
|
9
|
+
|
10
|
+
## What it does
|
11
|
+
|
12
|
+
Part mail room, part bulletin board, dsmq is a central location for sharing messages
|
13
|
+
between processes, even when they are running on computers scattered around the world.
|
14
|
+
|
15
|
+
Its defining characteristic is bare-bones simplicity.
|
16
|
+
|
17
|
+
## How to use it
|
18
|
+
|
19
|
+
### Install
|
20
|
+
|
21
|
+
```bash
|
22
|
+
pip install dsmq
|
23
|
+
```
|
24
|
+
### Create a dsmq server
|
25
|
+
|
26
|
+
As in `src/dsmq/example_server.py`
|
27
|
+
|
28
|
+
```python
|
29
|
+
from dsmq import dsmq
|
30
|
+
|
31
|
+
dsmq.start_server(host="127.0.0.1", port=30008)
|
32
|
+
```
|
33
|
+
|
34
|
+
### Connect a client to a dsmq server
|
35
|
+
|
36
|
+
As in `src/dsmq/example_put_client.py`
|
37
|
+
|
38
|
+
```python
|
39
|
+
mq = dsmq.connect_to_server(host="127.0.0.1", port=12345)
|
40
|
+
```
|
41
|
+
### Add a message to a queue
|
42
|
+
|
43
|
+
As in `src/dsmq/example_put_client.py`
|
44
|
+
|
45
|
+
```python
|
46
|
+
topic = "greetings"
|
47
|
+
msg = "hello world!"
|
48
|
+
mq.put(topic, msg)
|
49
|
+
```
|
50
|
+
|
51
|
+
### Read a message from a queue
|
52
|
+
|
53
|
+
As in `src/dsmq/example_get_client.py`
|
54
|
+
|
55
|
+
```python
|
56
|
+
topic = "greetings"
|
57
|
+
msg = mq.get(topic)
|
58
|
+
```
|
59
|
+
|
60
|
+
### Spin up and shut down a dsmq in its own process
|
61
|
+
|
62
|
+
A dsmq server doesn't come with a built-in way to shut itself down.
|
63
|
+
It can be helpful to have it running in a separate process that can be
|
64
|
+
managed
|
65
|
+
|
66
|
+
```python
|
67
|
+
import multiprocessing as mp
|
68
|
+
|
69
|
+
p_mq = mp.Process(target=dsmq.start_server, args=(config.MQ_HOST, config.MQ_PORT))
|
70
|
+
p_mq.start()
|
71
|
+
|
72
|
+
p_mq.join()
|
73
|
+
# or
|
74
|
+
p_mq.kill()
|
75
|
+
p_mq.close()
|
76
|
+
```
|
77
|
+
|
78
|
+
### Demo
|
79
|
+
|
80
|
+
1. Open 3 separate terminal windows.
|
81
|
+
1. In the first, run `src/dsmq/dsmq.py`.
|
82
|
+
1. In the second, run `src/dsmq/example_put_client.py`.
|
83
|
+
1. In the third, run `src/dsmq/example_get_client.py`.
|
84
|
+
|
85
|
+
Alternatively, if you're on Linux just run `src/dsmq/demo_linux.py`.
|
86
|
+
|
87
|
+
## How it works
|
88
|
+
|
89
|
+
### Expected behavior and limitations
|
90
|
+
|
91
|
+
- Many clients can read messages of the same topic. It is a one-to-many
|
92
|
+
publication model.
|
93
|
+
|
94
|
+
- A client will not be able to read any of the messages that were put into
|
95
|
+
a queue before it connected.
|
96
|
+
|
97
|
+
- A client will get the oldest message available on a requested topic.
|
98
|
+
Queues are first-in-first-out.
|
99
|
+
|
100
|
+
- Put and get operations are fairly quick--less than 100 $`\mu`$s of processing
|
101
|
+
time plus any network latency--so it can comfortably handle requests at rates of
|
102
|
+
hundreds of times per second. But if you have several clients reading and writing
|
103
|
+
at 1 kHz or more, you may overload the queue.
|
104
|
+
|
105
|
+
- The queue is backed by an in-memory SQLite database. If your message volumes
|
106
|
+
get larger than your RAM, you will reach an out-of-memory condition.
|
107
|
+
|
108
|
+
|
109
|
+
# API Reference
|
110
|
+
[[source](https://github.com/brohrer/dsmq/blob/main/src/dsmq/dsmq.py)]
|
111
|
+
|
112
|
+
### `start_server(host="127.0.0.1", port=30008)`
|
113
|
+
|
114
|
+
Kicks off the mesage queue server. This process will be the central exchange
|
115
|
+
for all incoming and outgoing messages.
|
116
|
+
- `host` (str), IP address on which the server will be visible and
|
117
|
+
- `port` (int), port. These will be used by all clients.
|
118
|
+
Non-privileged ports are numbered 1024 and higher.
|
119
|
+
|
120
|
+
### `connect_to_server(host="127.0.0.1", port=30008)`
|
121
|
+
|
122
|
+
Connects to an existing message queue server.
|
123
|
+
- `host` (str), IP address of the *server*.
|
124
|
+
- `port` (int), port on which the server is listening.
|
125
|
+
- returns a `DSMQClientSideConnection` object.
|
126
|
+
|
127
|
+
## `DSMQClientSideConnection` class
|
128
|
+
|
129
|
+
This is a convenience wrapper, to make the `get()` and `put()` functions
|
130
|
+
easy to write and remember.
|
131
|
+
|
132
|
+
### `put(topic, msg)`
|
133
|
+
|
134
|
+
Puts `msg` into the queue named `topic`. If the queue doesn't exist yet, it is created.
|
135
|
+
- msg (str), the content of the message.
|
136
|
+
- topic (str), name of the message queue in which to put this message.
|
137
|
+
|
138
|
+
### `get(topic)`
|
139
|
+
|
140
|
+
Get the oldest eligible message from the queue named `topic`.
|
141
|
+
The client is only elgibile to receive messages that were added after it
|
142
|
+
connected to the server.
|
143
|
+
- `topic` (str)
|
144
|
+
- returns str, the content of the message. If there was no eligble message
|
145
|
+
in the topic, or the topic doesn't yet exist,
|
146
|
+
returns "".
|
@@ -0,0 +1,10 @@
|
|
1
|
+
dsmq/__init__.py,sha256=YCgbnQAk8YbtHRyMcU0v2O7RdRhPhlT-vS_q40a7Q6g,50
|
2
|
+
dsmq/demo_linux.py,sha256=7yLglGmirDLuuyMxppYSK-dfx2Fg2Q0dIWB4cl2yV1c,622
|
3
|
+
dsmq/dsmq.py,sha256=5fo3wQfw2lIXiDGB05sRwVLDN0ZoUSw7e7WO8xOLXJY,5558
|
4
|
+
dsmq/example_get_client.py,sha256=chFfB2949PBENmgdUc3ASrATq1m4wvHGBzEnOC-o_Xs,296
|
5
|
+
dsmq/example_put_client.py,sha256=mUKCRhmUieMZEpHLFWFvzeKB6IR7A8l4tWN8TvPzdKU,348
|
6
|
+
dsmq/example_server.py,sha256=kkXOPaaTzVxf9_iIM76zU9pZhkPna_1vcGWkPrhCjus,61
|
7
|
+
dsmq-0.4.0.dist-info/METADATA,sha256=T3godcTW__I3b2hj2dgJCjMJEGEo6S1HP_Iq5EQD6Ok,3878
|
8
|
+
dsmq-0.4.0.dist-info/WHEEL,sha256=3U_NnUcV_1B1kPkYaPzN-irRckL5VW_lytn0ytO_kRY,87
|
9
|
+
dsmq-0.4.0.dist-info/licenses/LICENSE,sha256=3Yu1mAp5VsKmnDtzkiOY7BdmrLeNwwZ3t6iWaLnlL0Y,1071
|
10
|
+
dsmq-0.4.0.dist-info/RECORD,,
|
dsmq/py.typed
DELETED
File without changes
|
dsmq-0.2.0.dist-info/METADATA
DELETED
@@ -1,156 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.3
|
2
|
-
Name: dsmq
|
3
|
-
Version: 0.2.0
|
4
|
-
Summary: A dead simple message queue
|
5
|
-
License-File: LICENSE
|
6
|
-
Requires-Python: >=3.10
|
7
|
-
Description-Content-Type: text/markdown
|
8
|
-
|
9
|
-
# Dead Simple Message Queue
|
10
|
-
|
11
|
-
## What it does
|
12
|
-
|
13
|
-
Part mail room, part bulletin board, dsmq is a central location for sharing messages
|
14
|
-
between processes, even when they are running on computers scattered around the world.
|
15
|
-
|
16
|
-
Its defining characteristic is its bare-bones simplicity.
|
17
|
-
|
18
|
-
## How to use it
|
19
|
-
|
20
|
-
### Install
|
21
|
-
|
22
|
-
```bash
|
23
|
-
pip install dsmq
|
24
|
-
```
|
25
|
-
### Create a dsmq server
|
26
|
-
|
27
|
-
As in `src/dsmq/example_server.py`
|
28
|
-
|
29
|
-
```python
|
30
|
-
from dsmq import dsmq
|
31
|
-
|
32
|
-
dsmq.run(host="127.0.0.1", port=12345)
|
33
|
-
```
|
34
|
-
|
35
|
-
### Add a message to a queue
|
36
|
-
|
37
|
-
As in `src/dsmq/example_put_client.py`
|
38
|
-
|
39
|
-
```python
|
40
|
-
import json
|
41
|
-
import socket
|
42
|
-
|
43
|
-
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
44
|
-
s.connect(("127.0.0.1", 12345))
|
45
|
-
message_content = {"action": "put", "topic": "greetings", "message": "Hello!"}
|
46
|
-
msg = json.dumps(message_content)
|
47
|
-
s.sendall(bytes(msg, "utf-8"))
|
48
|
-
```
|
49
|
-
|
50
|
-
### Read a message from a queue
|
51
|
-
|
52
|
-
As in `src/dsmq/example_get_client.py`
|
53
|
-
|
54
|
-
```python
|
55
|
-
import json
|
56
|
-
import socket
|
57
|
-
|
58
|
-
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
59
|
-
s.connect(("127.0.0.1", 12345))
|
60
|
-
|
61
|
-
for i in range(n_iter):
|
62
|
-
request_message_content = {"action": "get", "topic": "greetings"}
|
63
|
-
request_msg = json.dumps(request_message_content)
|
64
|
-
s.sendall(bytes(reequest_msg, "utf-8"))
|
65
|
-
|
66
|
-
reply_msg = s.recv(1024)
|
67
|
-
if not reply_msg:
|
68
|
-
raise RuntimeError("Connection terminated by server")
|
69
|
-
reply_msg_content = reply_msg.decode("utf-8")
|
70
|
-
```
|
71
|
-
|
72
|
-
### Demo
|
73
|
-
|
74
|
-
1. Open 3 separate terminal windows.
|
75
|
-
1. In the first, run `src/dsmq/dsmq.py`.
|
76
|
-
1. In the second, run `src/dsmq/example_put_client.py`.
|
77
|
-
1. In the third, run `src/dsmq/example_get_client.py`.
|
78
|
-
|
79
|
-
|
80
|
-
## How it works
|
81
|
-
|
82
|
-
### Expected behavior and limitations
|
83
|
-
|
84
|
-
- Many clients can read messages of the same topic. It is a one-to-many
|
85
|
-
pulication model.
|
86
|
-
|
87
|
-
- A client will not be able to read any of the messages that were put into
|
88
|
-
a queue before it connected.
|
89
|
-
|
90
|
-
- A client will get the oldest message available on a requested topic.
|
91
|
-
Queues are first-in-first-out.
|
92
|
-
|
93
|
-
- Put and get operations are fairly quick--less than 100 $`\mu`$s of processing
|
94
|
-
time plus any network latency--so it can comfortably handle operations at
|
95
|
-
hundreds of Hz. But if you try to have several clients reading and writing
|
96
|
-
at 1 kHz or more, you may overload the queue.
|
97
|
-
|
98
|
-
- The queue is backed by an in-memory SQLite database. If your message volumes
|
99
|
-
get larger than your RAM, you may reach an out-of-memory condition.
|
100
|
-
|
101
|
-
|
102
|
-
# API Reference and Cookbook
|
103
|
-
[[source](https://github.com/brohrer/dsmq/blob/main/src/dsmq/dsmq.py)]
|
104
|
-
|
105
|
-
### Start a server
|
106
|
-
|
107
|
-
```python
|
108
|
-
run(host="127.0.0.1", port=30008)
|
109
|
-
```
|
110
|
-
|
111
|
-
Kicks off the mesage queue server. This process will be the central exchange
|
112
|
-
for all incoming and outgoing messages.
|
113
|
-
- `host` (str), IP address on which the server will be visible and
|
114
|
-
- `port` (int), port. These will be used by all clients.
|
115
|
-
Non-privileged ports are numbered 1024 and higher.
|
116
|
-
|
117
|
-
### Open a connection from a client
|
118
|
-
|
119
|
-
```python
|
120
|
-
import socket
|
121
|
-
|
122
|
-
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
123
|
-
s.connect((host, port ))
|
124
|
-
```
|
125
|
-
|
126
|
-
### Add a message to a queue
|
127
|
-
|
128
|
-
```python
|
129
|
-
import json
|
130
|
-
msg = json.dumps({
|
131
|
-
"action": "put",
|
132
|
-
"topic": <queue-name>,
|
133
|
-
"message": <message-content>
|
134
|
-
})
|
135
|
-
s.sendall(bytes(msg, "utf-8"))
|
136
|
-
```
|
137
|
-
|
138
|
-
- `s`, the socket connection to the server
|
139
|
-
- `<queue-name>` (str), a name for the queue where the message will be added
|
140
|
-
- `<message-content>` (str), whatever message content you want
|
141
|
-
|
142
|
-
Place `message-content` into the queue named `queue-name`.
|
143
|
-
If the queue doesn't exist yet, create it.
|
144
|
-
|
145
|
-
### Get a message from a queue
|
146
|
-
|
147
|
-
```python
|
148
|
-
request_msg = json.dumps({"action": "get", "topic": <queue-name>})
|
149
|
-
s.sendall(bytes(request_msg, "utf-8"))
|
150
|
-
data = s.recv(1024)
|
151
|
-
msg_str = data.decode("utf-8")
|
152
|
-
```
|
153
|
-
|
154
|
-
Get the oldest eligible message from the queue named `<queue-name>`.
|
155
|
-
The client is only elgibile to receive messages added after it
|
156
|
-
connected to the server.
|
dsmq-0.2.0.dist-info/RECORD
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
dsmq/__init__.py,sha256=YCgbnQAk8YbtHRyMcU0v2O7RdRhPhlT-vS_q40a7Q6g,50
|
2
|
-
dsmq/demo_linux.py,sha256=D5NjEvPG-fTGRRbYzMJXgtwfrigOt_eaR6D0AozBNbA,613
|
3
|
-
dsmq/dsmq.py,sha256=EJW6tMeG57QYBneqBEM4R_5CrtG-Lx2qz0D1BM0YtjI,4452
|
4
|
-
dsmq/example_get_client.py,sha256=ZWZrAu7sIEn8VwCK09cTwsm1XnF3Jkor3r7Gl4ljUMw,630
|
5
|
-
dsmq/example_put_client.py,sha256=bSjSJZsFZB6inVGUlTnJn0jgrpVNXvrN46STTvgwCzs,497
|
6
|
-
dsmq/example_server.py,sha256=UM69GN7uORDetXQM8LlW68nrA-1i7Jgp2dZREVEqxaU,62
|
7
|
-
dsmq/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
-
dsmq-0.2.0.dist-info/METADATA,sha256=TW6tM_oF2LuyRrmOBSBTHFCkJvNDA8O3QfuEtgD1-S8,4055
|
9
|
-
dsmq-0.2.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
10
|
-
dsmq-0.2.0.dist-info/licenses/LICENSE,sha256=3Yu1mAp5VsKmnDtzkiOY7BdmrLeNwwZ3t6iWaLnlL0Y,1071
|
11
|
-
dsmq-0.2.0.dist-info/RECORD,,
|
File without changes
|