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 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.run, args=(HOST, PORT))
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 run(host="127.0.0.1", port=30008):
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=handle_socket, args=(socket_conn,)).start()
45
+ Thread(target=_handle_client_connection, args=(socket_conn,)).start()
38
46
 
39
47
  sqlite_conn.close()
40
48
 
41
49
 
42
- def handle_socket(socket_conn):
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
- run(host=host, port=port)
179
+ start_server(host=host, port=port)
144
180
  elif len(sys.argv) == 2:
145
181
  host = sys.argv[1]
146
- run(host=host)
182
+ start_server(host=host)
147
183
  elif len(sys.argv) == 1:
148
- run()
184
+ start_server()
149
185
  else:
150
186
  print(
151
187
  """
@@ -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
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
8
- s.connect((host, port))
6
+ mq = dsmq.connect_to_server(host=host, port=port)
9
7
 
10
- for i in range(n_iter):
11
- time.sleep(1)
12
- msg = json.dumps({"action": "get", "topic": "greetings"})
13
- s.sendall(bytes(msg, "utf-8"))
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__":
@@ -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
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
8
- s.connect((host, port))
6
+ mq = dsmq.connect_to_server(host=host, port=port)
9
7
 
10
- for i in range(n_iter):
11
- time.sleep(1)
12
- note = f"{i}. Hello, world"
13
- msg = json.dumps({"action": "put", "topic": "greetings", "message": note})
14
- s.sendall(bytes(msg, "utf-8"))
15
- print(f"client sent {msg}")
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
- from dsmq import dsmq
1
+ import dsmq
2
2
 
3
- dsmq.run(host="127.0.0.1", port=12345)
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,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.25.0
2
+ Generator: hatchling 1.26.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
dsmq/py.typed DELETED
File without changes
@@ -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.
@@ -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,,