dsmq 0.6.0__py3-none-any.whl → 0.7.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/dsmq.py CHANGED
@@ -5,15 +5,17 @@ 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
8
+ _default_host = "127.0.0.1"
9
+ _default_port = 30008
10
10
 
11
- N_RETRIES = 5
12
- FIRST_RETRY = 0.01 # seconds
13
- TIME_TO_LIVE = 600.0 # seconds
11
+ _message_length_offset = 1_000_000
12
+ _header_length = 23
13
+ _n_retries = 5
14
+ _first_retry = 0.01 # seconds
15
+ _time_to_live = 600.0 # seconds
14
16
 
15
17
 
16
- def start_server(host=DEFAULT_HOST, port=DEFAULT_PORT):
18
+ def start_server(host=_default_host, port=_default_port):
17
19
  """
18
20
  For best results, start this running in its own process and walk away.
19
21
  """
@@ -21,7 +23,7 @@ def start_server(host=DEFAULT_HOST, port=DEFAULT_PORT):
21
23
  cursor = sqlite_conn.cursor()
22
24
 
23
25
  cursor.execute("""
24
- CREATE TABLE IF NOT EXISTS messages (timestamp DOUBLE, topic TEXT, message TEXT)
26
+ CREATE TABLE IF NOT EXISTS messages (timestamp DOUBLE, topic TEXT, message TEXT)
25
27
  """)
26
28
 
27
29
  with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
@@ -46,7 +48,7 @@ def start_server(host=DEFAULT_HOST, port=DEFAULT_PORT):
46
48
  sqlite_conn.close()
47
49
 
48
50
 
49
- def connect_to_server(host=DEFAULT_HOST, port=DEFAULT_PORT):
51
+ def connect_to_server(host=_default_host, port=_default_port):
50
52
  return DSMQClientSideConnection(host, port)
51
53
 
52
54
 
@@ -56,22 +58,55 @@ class DSMQClientSideConnection:
56
58
  self.dsmq_conn.connect((host, port))
57
59
 
58
60
  def get(self, topic):
59
- msg = json.dumps({"action": "get", "topic": topic})
60
- self.dsmq_conn.sendall(bytes(msg, "utf-8"))
61
+ msg_dict = {"action": "get", "topic": topic}
62
+ _send_message(self.dsmq_conn, msg_dict)
61
63
 
62
- data = self.dsmq_conn.recv(1024)
63
- if not data:
64
+ msg = _receive_message(self.dsmq_conn)
65
+ if msg is None:
64
66
  raise RuntimeError("Connection terminated by server")
65
- msg_str = data.decode("utf-8")
66
- msg = json.loads(msg_str)
67
- try:
68
- return msg["message"]
69
- except KeyError:
70
- return ""
67
+ return msg["message"]
71
68
 
72
- def put(self, topic, msg):
73
- msg = json.dumps({"action": "put", "topic": topic, "message": msg})
74
- self.dsmq_conn.sendall(bytes(msg, "utf-8"))
69
+ def put(self, topic, msg_body):
70
+ msg_dict = {"action": "put", "topic": topic, "message": msg_body}
71
+ _send_message(self.dsmq_conn, msg_dict)
72
+
73
+
74
+ def _send_message(socket_conn, msg_dict):
75
+ msg = json.dumps(msg_dict)
76
+ msg_bytes = bytes(msg, "utf-8")
77
+ n_bytes = len(msg_bytes)
78
+
79
+ # Send a header first.
80
+ # Add a large number to ensure that the message is the same size each time.
81
+ header_dict = {"msg_length": n_bytes + _message_length_offset}
82
+ header = json.dumps(header_dict)
83
+ header_bytes = bytes(header, "utf-8")
84
+ socket_conn.sendall(header_bytes)
85
+
86
+ socket_conn.sendall(msg_bytes)
87
+
88
+
89
+ def _receive_message(socket_conn):
90
+ # First receive a header
91
+ header_data = socket_conn.recv(_header_length)
92
+ if header_data is None:
93
+ return None
94
+ if len(header_data) == 0:
95
+ return None
96
+
97
+ header_str = header_data.decode("utf-8")
98
+ header = json.loads(header_str)
99
+ msg_length = header["msg_length"] - _message_length_offset
100
+
101
+ data = socket_conn.recv(msg_length)
102
+ if data is None:
103
+ return None
104
+ if len(data) == 0:
105
+ return None
106
+
107
+ msg_str = data.decode("utf-8")
108
+ msg = json.loads(msg_str)
109
+ return msg
75
110
 
76
111
 
77
112
  def _handle_client_connection(socket_conn):
@@ -83,19 +118,10 @@ def _handle_client_connection(socket_conn):
83
118
  time_of_last_purge = time.time()
84
119
 
85
120
  while True:
86
- data = socket_conn.recv(1024)
87
- # Check whether the connection has been terminated
88
- if not data:
121
+ msg = _receive_message(socket_conn)
122
+ if msg is None:
89
123
  break
90
124
 
91
- msg_str = data.decode("utf-8")
92
- try:
93
- msg = json.loads(msg_str)
94
- except json.decoder.JSONDecodeError:
95
- print("Message must be json-friendly")
96
- print(f" Received: {msg}")
97
- continue
98
-
99
125
  topic = msg["topic"]
100
126
  timestamp = time.time()
101
127
 
@@ -104,7 +130,7 @@ def _handle_client_connection(socket_conn):
104
130
 
105
131
  # This block allows for multiple retries if the database
106
132
  # is busy.
107
- for i_retry in range(N_RETRIES):
133
+ for i_retry in range(_n_retries):
108
134
  try:
109
135
  cursor.execute(
110
136
  """
@@ -115,7 +141,7 @@ VALUES (:timestamp, :topic, :message)
115
141
  )
116
142
  sqlite_conn.commit()
117
143
  except sqlite3.OperationalError:
118
- wait_time = FIRST_RETRY * 2**i_retry
144
+ wait_time = _first_retry * 2**i_retry
119
145
  time.sleep(wait_time)
120
146
  continue
121
147
  break
@@ -130,7 +156,7 @@ VALUES (:timestamp, :topic, :message)
130
156
 
131
157
  # This block allows for multiple retries if the database
132
158
  # is busy.
133
- for i_retry in range(N_RETRIES):
159
+ for i_retry in range(_n_retries):
134
160
  try:
135
161
  cursor.execute(
136
162
  """
@@ -149,7 +175,7 @@ AND timestamp = a.min_time
149
175
  msg,
150
176
  )
151
177
  except sqlite3.OperationalError:
152
- wait_time = FIRST_RETRY * 2**i_retry
178
+ wait_time = _first_retry * 2**i_retry
153
179
  time.sleep(wait_time)
154
180
  continue
155
181
  break
@@ -163,15 +189,14 @@ AND timestamp = a.min_time
163
189
  # Handle the case where no results are returned
164
190
  message = ""
165
191
 
166
- msg = json.dumps({"message": message})
167
- socket_conn.sendall(bytes(msg, "utf-8"))
192
+ _send_message(socket_conn, {"message": message})
168
193
  else:
169
194
  print("Action must either be 'put' or 'get'")
170
195
 
171
196
  # Periodically clean out messages from the queue that are
172
197
  # past their sell buy date.
173
198
  # This operation is pretty fast. I clock it at 12 us on my machine.
174
- if time.time() - time_of_last_purge > TIME_TO_LIVE:
199
+ if time.time() - time_of_last_purge > _time_to_live:
175
200
  cursor.execute(
176
201
  """
177
202
  DELETE FROM messages
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dsmq
3
- Version: 0.6.0
3
+ Version: 0.7.0
4
4
  Summary: A dead simple message queue
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -1,10 +1,10 @@
1
1
  dsmq/__init__.py,sha256=YCgbnQAk8YbtHRyMcU0v2O7RdRhPhlT-vS_q40a7Q6g,50
2
2
  dsmq/demo_linux.py,sha256=7yLglGmirDLuuyMxppYSK-dfx2Fg2Q0dIWB4cl2yV1c,622
3
- dsmq/dsmq.py,sha256=zg69Tf3x9z26a9NrEXTWgr78kcNKVszmLq0INv3IHuM,6132
3
+ dsmq/dsmq.py,sha256=f-dR-Pjjn_gDx-EK1evYQWvDq8ypXbhx6DaCWHCInSc,6744
4
4
  dsmq/example_get_client.py,sha256=chFfB2949PBENmgdUc3ASrATq1m4wvHGBzEnOC-o_Xs,296
5
5
  dsmq/example_put_client.py,sha256=mUKCRhmUieMZEpHLFWFvzeKB6IR7A8l4tWN8TvPzdKU,348
6
6
  dsmq/example_server.py,sha256=kkXOPaaTzVxf9_iIM76zU9pZhkPna_1vcGWkPrhCjus,61
7
- dsmq-0.6.0.dist-info/METADATA,sha256=0mL4N2uHDGtlnH_fqrdNLdpS-flp6x1O1g-uLGzF4b4,4006
8
- dsmq-0.6.0.dist-info/WHEEL,sha256=3U_NnUcV_1B1kPkYaPzN-irRckL5VW_lytn0ytO_kRY,87
9
- dsmq-0.6.0.dist-info/licenses/LICENSE,sha256=3Yu1mAp5VsKmnDtzkiOY7BdmrLeNwwZ3t6iWaLnlL0Y,1071
10
- dsmq-0.6.0.dist-info/RECORD,,
7
+ dsmq-0.7.0.dist-info/METADATA,sha256=NFG4GQwX9RqIKLR3xvFelzuTVebieS4sJqZ9uFu24Lw,4006
8
+ dsmq-0.7.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
9
+ dsmq-0.7.0.dist-info/licenses/LICENSE,sha256=3Yu1mAp5VsKmnDtzkiOY7BdmrLeNwwZ3t6iWaLnlL0Y,1071
10
+ dsmq-0.7.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.26.1
2
+ Generator: hatchling 1.26.3
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any