dsmq 1.3.0__py3-none-any.whl → 1.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/.client.py.swp ADDED
Binary file
dsmq/.server.py.swp CHANGED
Binary file
dsmq/client.py CHANGED
@@ -18,6 +18,7 @@ def connect(host=_default_host, port=_default_port, verbose=False):
18
18
  class DSMQClientSideConnection:
19
19
  def __init__(self, host, port, verbose=False):
20
20
  self.uri = f"ws://{host}:{port}"
21
+ self.port = port
21
22
  self.verbose = verbose
22
23
  if self.verbose:
23
24
  print(f"Connecting to dsmq server at {self.uri}")
@@ -40,13 +41,41 @@ class DSMQClientSideConnection:
40
41
 
41
42
  def get(self, topic):
42
43
  msg = {"action": "get", "topic": topic}
43
- self.websocket.send(json.dumps(msg))
44
+ try:
45
+ self.websocket.send(json.dumps(msg))
46
+ except ConnectionClosedError:
47
+ return ""
48
+
49
+ try:
50
+ msg_text = self.websocket.recv()
51
+ except ConnectionClosedError:
52
+ self.close()
53
+ return ""
54
+
55
+ msg = json.loads(msg_text)
56
+ return msg["message"]
57
+
58
+ def get_latest(self, topic):
59
+ """
60
+ A variant of `get()` that grabs the latest available message
61
+ (if there is one) rather than grabbing the oldest unread message.
62
+ It will not go back to read older ones on subsequent calls;
63
+ it will leave them unread.
64
+ """
65
+ msg = {"action": "get_latest", "topic": topic}
66
+ try:
67
+ self.websocket.send(json.dumps(msg))
68
+ except ConnectionClosedError:
69
+ return ""
70
+
44
71
  try:
45
72
  msg_text = self.websocket.recv()
46
73
  except ConnectionClosedError:
47
74
  self.close()
75
+ return ""
48
76
 
49
77
  msg = json.loads(msg_text)
78
+
50
79
  return msg["message"]
51
80
 
52
81
  def get_wait(self, topic):
@@ -64,7 +93,10 @@ class DSMQClientSideConnection:
64
93
 
65
94
  def put(self, topic, msg_body):
66
95
  msg_dict = {"action": "put", "topic": topic, "message": msg_body}
67
- self.websocket.send(json.dumps(msg_dict))
96
+ try:
97
+ self.websocket.send(json.dumps(msg_dict))
98
+ except ConnectionClosedError:
99
+ return
68
100
 
69
101
  def shutdown_server(self):
70
102
  msg_dict = {"action": "shutdown", "topic": ""}
dsmq/demo.py CHANGED
@@ -1,7 +1,12 @@
1
1
  import multiprocessing as mp
2
- from dsmq.server import serve
3
- import dsmq.example_get_client
4
- import dsmq.example_put_client
2
+
3
+ # spawn is the default method on macOS,
4
+ # starting in Python 3.14 it will be the default in Linux too.
5
+ mp.set_start_method("spawn")
6
+
7
+ from dsmq.server import serve # noqa: E402
8
+ import dsmq.example_get_client # noqa: E402
9
+ import dsmq.example_put_client # noqa: E402
5
10
 
6
11
  HOST = "127.0.0.1"
7
12
  PORT = 25252
dsmq/server.py CHANGED
@@ -9,48 +9,47 @@ from websockets.exceptions import ConnectionClosedError, ConnectionClosedOK
9
9
 
10
10
  _default_host = "127.0.0.1"
11
11
  _default_port = 30008
12
- _n_retries = 20
13
- _first_retry = 0.005 # seconds
14
- _time_to_live = 60.0 # seconds
15
-
16
- # _db_name = ":memory:"
17
- _db_name = "file::memory:?cache=shared"
18
- # May occasionally create files with this name.
19
- # https://sqlite.org/inmemorydb.html
20
- # "...parts of a temporary database might be flushed to disk if the
21
- # database becomes large or if SQLite comes under memory pressure."
12
+ _max_queue_length = 10
13
+ _shutdown_pause = 1.0 # seconds
14
+ _time_between_cleanup = 3.0 # seconds
15
+ _time_to_keep = 0.3 # seconds
22
16
 
23
17
  # Make this global so it's easy to share
24
18
  dsmq_server = None
25
19
 
26
20
 
27
- def serve(host=_default_host, port=_default_port, verbose=False):
21
+ def serve(
22
+ host=_default_host,
23
+ port=_default_port,
24
+ name="mqdb",
25
+ verbose=False,
26
+ ):
28
27
  """
29
28
  For best results, start this running in its own process and walk away.
30
29
  """
31
- # Cleanup temp files.
32
- # Under some condition
33
- # (which I haven't yet been able to pin down)
34
- # a file is generated with the db name.
35
- # If it is not removed, it gets
36
- # treated as a SQLite db on disk,
37
- # which dramatically slows it down,
38
- # especially the way it's used here for
39
- # rapid-fire one-item reads and writes.
40
- filenames = os.listdir()
41
- for filename in filenames:
42
- if filename[: len(_db_name)] == _db_name:
43
- os.remove(filename)
44
-
30
+ # May occasionally create files with this name.
31
+ # https://sqlite.org/inmemorydb.html
32
+ # "...parts of a temporary database might be flushed to disk if the
33
+ # database becomes large or if SQLite comes under memory pressure."
34
+ global _db_name
35
+ _db_name = f"file:{name}?mode=memory&cache=shared"
36
+
37
+ cleanup_temp_files()
45
38
  sqlite_conn = sqlite3.connect(_db_name)
46
39
  cursor = sqlite_conn.cursor()
47
40
 
48
41
  # Tweak the connection to make it faster
49
42
  # and keep long-term latency more predictable.
50
- # cursor.execute("PRAGMA journal_mode = OFF")
51
- cursor.execute("PRAGMA journal_mode = WAL")
52
- # cursor.execute("PRAGMA synchronous = OFF")
53
- # cursor.execute("PRAGMA secure_delete = OFF")
43
+ # These also make it more susceptible to corruption during shutdown,
44
+ # but since dsmq is meant to be ephemeral, that's not a concern.
45
+ # See https://www.sqlite.org/pragma.html
46
+ #
47
+ cursor.execute("PRAGMA journal_mode = OFF")
48
+ # cursor.execute("PRAGMA journal_mode = MEMORY")
49
+ # cursor.execute("PRAGMA synchronous = NORMAL")
50
+ cursor.execute("PRAGMA synchronous = OFF")
51
+ cursor.execute("PRAGMA secure_delete = OFF")
52
+ cursor.execute("PRAGMA temp_store = MEMORY")
54
53
 
55
54
  cursor.execute("""
56
55
  CREATE TABLE IF NOT EXISTS messages (timestamp DOUBLE, topic TEXT, message TEXT)
@@ -58,44 +57,60 @@ CREATE TABLE IF NOT EXISTS messages (timestamp DOUBLE, topic TEXT, message TEXT)
58
57
 
59
58
  # Making this global in scope is a way to make it available
60
59
  # to the shutdown operation. It's an awkward construction,
61
- # and a method of last resort. (If you stumble across this and
62
- # figure out something more elegant, please submit a PR!
63
- # or send it to me at brohrer@gmail.com,
60
+ # and a method of last resort.
64
61
  global dsmq_server
65
62
 
66
- # dsmq_server = ws_serve(request_handler, host, port)
67
- for i_retry in range(_n_retries):
68
- try:
63
+ try:
64
+ with ws_serve(request_handler, host, port) as dsmq_server:
65
+ dsmq_server.serve_forever()
66
+
67
+ except OSError:
68
+ # Catch the case where the address is already in use
69
+ if verbose:
70
+ print()
71
+ print(f"Found a dsmq server already running on {host} on port {port}.")
72
+ print(" Closing it down.")
73
+
74
+ def shutdown_gracefully(server_to_shutdown):
75
+ server_to_shutdown.shutdown()
76
+
77
+ Thread(target=shutdown_gracefully, args=(dsmq_server,)).start()
78
+ time.sleep(_shutdown_pause)
79
+
69
80
  with ws_serve(request_handler, host, port) as dsmq_server:
70
81
  dsmq_server.serve_forever()
71
82
 
72
- if verbose:
73
- print()
74
- print(f"Server started at {host} on port {port}.")
75
- print("Waiting for clients...")
76
-
77
- break
78
-
79
- except OSError:
80
- # Catch the case where the address is already in use
81
- if verbose:
82
- print()
83
- if i_retry < _n_retries - 1:
84
- print(f"Couldn't start dsmq server on {host} on port {port}.")
85
- print(f" Trying again ({i_retry}) ...")
86
- else:
87
- print()
88
- print(f"Failed to start dsmq server on {host} on port {port}.")
89
- print()
90
- raise
91
-
92
- wait_time = _first_retry * 2**i_retry
93
- time.sleep(wait_time)
83
+ if verbose:
84
+ print()
85
+ print(f"Server started at {host} on port {port}.")
86
+ print("Waiting for clients...")
94
87
 
95
88
  sqlite_conn.close()
89
+ time.sleep(_shutdown_pause)
90
+ cleanup_temp_files()
91
+
92
+
93
+ def cleanup_temp_files():
94
+ # Under some condition
95
+ # (which I haven't yet been able to pin down)
96
+ # a file is generated with the db name.
97
+ # If it is not removed, it gets
98
+ # treated as a SQLite db on disk,
99
+ # which dramatically slows it down,
100
+ # especially the way it's used here for
101
+ # rapid-fire one-item reads and writes.
102
+ global _db_name
103
+ filenames = os.listdir()
104
+ for filename in filenames:
105
+ if filename[: len(_db_name)] == _db_name:
106
+ try:
107
+ os.remove(filename)
108
+ except FileNotFoundError:
109
+ pass
96
110
 
97
111
 
98
112
  def request_handler(websocket):
113
+ global _db_name
99
114
  sqlite_conn = sqlite3.connect(_db_name)
100
115
  cursor = sqlite_conn.cursor()
101
116
 
@@ -107,30 +122,25 @@ def request_handler(websocket):
107
122
  for msg_text in websocket:
108
123
  msg = json.loads(msg_text)
109
124
  topic = msg["topic"]
125
+ action = msg["action"]
110
126
  timestamp = time.time()
111
127
 
112
- if msg["action"] == "put":
128
+ if action == "put":
113
129
  msg["timestamp"] = timestamp
114
130
 
115
- # This block allows for multiple retries if the database
116
- # is busy.
117
- for i_retry in range(_n_retries):
118
- try:
119
- cursor.execute(
120
- """
121
- INSERT INTO messages (timestamp, topic, message)
122
- VALUES (:timestamp, :topic, :message)
123
- """,
124
- (msg),
125
- )
126
- sqlite_conn.commit()
127
- except sqlite3.OperationalError:
128
- wait_time = _first_retry * 2**i_retry
129
- time.sleep(wait_time)
130
- continue
131
- break
132
-
133
- elif msg["action"] == "get":
131
+ try:
132
+ cursor.execute(
133
+ """
134
+ INSERT INTO messages (timestamp, topic, message)
135
+ VALUES (:timestamp, :topic, :message)
136
+ """,
137
+ (msg),
138
+ )
139
+ sqlite_conn.commit()
140
+ except sqlite3.OperationalError:
141
+ pass
142
+
143
+ elif action == "get":
134
144
  try:
135
145
  last_read_time = last_read_times[topic]
136
146
  except KeyError:
@@ -138,31 +148,21 @@ def request_handler(websocket):
138
148
  last_read_time = last_read_times[topic]
139
149
  msg["last_read_time"] = last_read_time
140
150
 
141
- # This block allows for multiple retries if the database
142
- # is busy.
143
- for i_retry in range(_n_retries):
144
- try:
145
- cursor.execute(
146
- """
147
- SELECT message,
148
- timestamp
149
- FROM messages,
150
- (
151
- SELECT MIN(timestamp) AS min_time
152
- FROM messages
153
- WHERE topic = :topic
154
- AND timestamp > :last_read_time
155
- ) a
156
- WHERE topic = :topic
157
- AND timestamp = a.min_time
158
- """,
159
- msg,
160
- )
161
- except sqlite3.OperationalError:
162
- wait_time = _first_retry * 2**i_retry
163
- time.sleep(wait_time)
164
- continue
165
- break
151
+ try:
152
+ cursor.execute(
153
+ """
154
+ SELECT message,
155
+ timestamp
156
+ FROM messages
157
+ WHERE topic = :topic
158
+ AND timestamp > :last_read_time
159
+ ORDER BY timestamp ASC
160
+ LIMIT 1
161
+ """,
162
+ msg,
163
+ )
164
+ except sqlite3.OperationalError:
165
+ pass
166
166
 
167
167
  try:
168
168
  result = cursor.fetchall()[0]
@@ -174,45 +174,105 @@ def request_handler(websocket):
174
174
  message = ""
175
175
 
176
176
  websocket.send(json.dumps({"message": message}))
177
- elif msg["action"] == "shutdown":
177
+
178
+ elif action == "get_latest":
179
+ try:
180
+ last_read_time = last_read_times[topic]
181
+ except KeyError:
182
+ last_read_times[topic] = client_creation_time
183
+ last_read_time = last_read_times[topic]
184
+ msg["last_read_time"] = last_read_time
185
+
186
+ try:
187
+ cursor.execute(
188
+ """
189
+ SELECT message,
190
+ timestamp
191
+ FROM messages
192
+ WHERE topic = :topic
193
+ AND timestamp > :last_read_time
194
+ ORDER BY timestamp DESC
195
+ LIMIT 1;
196
+ """,
197
+ msg,
198
+ )
199
+ except sqlite3.OperationalError:
200
+ pass
201
+
202
+ try:
203
+ result = cursor.fetchall()[0]
204
+ message = result[0]
205
+ timestamp = result[1]
206
+ last_read_times[topic] = timestamp
207
+ except IndexError:
208
+ # Handle the case where no results are returned
209
+ message = ""
210
+
211
+ websocket.send(json.dumps({"message": message}))
212
+
213
+ elif action == "shutdown":
178
214
  # Run this from a separate thread to prevent deadlock
179
215
  global dsmq_server
180
216
 
181
217
  def shutdown_gracefully(server_to_shutdown):
182
218
  server_to_shutdown.shutdown()
183
219
 
184
- filenames = os.listdir()
185
- for filename in filenames:
186
- if filename[: len(_db_name)] == _db_name:
187
- try:
188
- os.remove(filename)
189
- except FileNotFoundError:
190
- pass
191
-
192
220
  Thread(target=shutdown_gracefully, args=(dsmq_server,)).start()
193
221
  break
194
222
  else:
195
223
  raise RuntimeWarning(
196
- "dsmq client action must either be 'put', 'get', or 'shutdown'"
224
+ "dsmq client action must either be\n"
225
+ + "'put', 'get', 'get_wait', 'get_latest', or 'shutdown'"
197
226
  )
198
227
 
199
- # Periodically clean out messages from the queue that are
200
- # past their sell buy date.
201
- # This operation is pretty fast. I clock it at 12 us on my machine.
202
- if time.time() - time_of_last_purge > _time_to_live:
228
+ # Periodically clean out messages to keep individual queues at
229
+ # a manageable length and the overall mq small.
230
+ if time.time() - time_of_last_purge > _time_between_cleanup:
231
+ cutoff_time = time.time() - _time_to_keep
203
232
  try:
204
233
  cursor.execute(
205
234
  """
206
- DELETE FROM messages
207
- WHERE timestamp < :time_threshold
235
+ DELETE
236
+ FROM messages
237
+ WHERE topic = :topic
238
+ AND timestamp < :cutoff_time
208
239
  """,
209
- {"time_threshold": time_of_last_purge},
240
+ {
241
+ "cutoff_time": cutoff_time,
242
+ "topic": topic,
243
+ },
210
244
  )
211
245
  sqlite_conn.commit()
212
246
  time_of_last_purge = time.time()
247
+
248
+ cursor.execute(
249
+ """
250
+ DELETE
251
+ FROM messages
252
+ WHERE topic = :topic
253
+ AND timestamp IN (
254
+ SELECT timestamp
255
+ FROM (
256
+ SELECT timestamp,
257
+ RANK() OVER (ORDER BY timestamp DESC) recency_rank
258
+ FROM messages
259
+ WHERE topic = :topic
260
+ )
261
+ WHERE recency_rank >= :max_queue_length
262
+ )
263
+ """,
264
+ {
265
+ "max_queue_length": _max_queue_length,
266
+ "topic": topic,
267
+ },
268
+ )
269
+ sqlite_conn.commit()
270
+ time_of_last_purge = time.time()
271
+
213
272
  except sqlite3.OperationalError:
214
273
  # Database may be locked. Try again next time.
215
274
  pass
275
+
216
276
  except (ConnectionClosedError, ConnectionClosedOK):
217
277
  # Something happened on the other end and this handler
218
278
  # is no longer needed.
@@ -1,10 +1,17 @@
1
1
  import multiprocessing as mp
2
+
3
+ try:
4
+ # spawn is the default method on macOS,
5
+ # starting in Python 3.14 it will be the default in Linux too.
6
+ mp.set_start_method("spawn")
7
+ except RuntimeError:
8
+ # Will throw an error if the start method has alraedy been set.
9
+ pass
10
+
2
11
  import time
3
12
  from dsmq.server import serve
4
13
  from dsmq.client import connect
5
14
 
6
- # spawn is the default method on macOS
7
- # mp.set_start_method('spawn')
8
15
 
9
16
  host = "127.0.0.1"
10
17
  port = 30303
@@ -53,6 +60,10 @@ def test_write_one_read_one():
53
60
  write_client = connect(host, port)
54
61
  read_client = connect(host, port)
55
62
 
63
+ msg = read_client.get("test")
64
+
65
+ assert msg == ""
66
+
56
67
  write_client.put("test", "test_msg")
57
68
 
58
69
  # It takes a moment for the write to complete
@@ -61,6 +72,10 @@ def test_write_one_read_one():
61
72
 
62
73
  assert msg == "test_msg"
63
74
 
75
+ msg = read_client.get("test")
76
+
77
+ assert msg == ""
78
+
64
79
  write_client.shutdown_server()
65
80
  write_client.close()
66
81
  read_client.close()
@@ -83,6 +98,29 @@ def test_get_wait():
83
98
  read_client.close()
84
99
 
85
100
 
101
+ def test_get_latest():
102
+ p_server = mp.Process(target=serve, args=(host, port))
103
+ p_server.start()
104
+ write_client = connect(host, port)
105
+ read_client = connect(host, port)
106
+
107
+ for i in range(5):
108
+ write_client.put("test", f"test_msg {i}")
109
+
110
+ time.sleep(_pause)
111
+ msg = read_client.get_latest("test")
112
+
113
+ assert msg == "test_msg 4"
114
+
115
+ msg = read_client.get_latest("test")
116
+
117
+ assert msg == ""
118
+
119
+ write_client.shutdown_server()
120
+ write_client.close()
121
+ read_client.close()
122
+
123
+
86
124
  def test_multitopics():
87
125
  p_server = mp.Process(target=serve, args=(host, port))
88
126
  p_server.start()
@@ -183,15 +221,8 @@ def test_speed_writing():
183
221
  p_speed_write.start()
184
222
  time.sleep(_pause)
185
223
 
186
- # time_a = time.time()
187
224
  write_client.put("test", "test_msg")
188
- # time_b = time.time()
189
225
  msg = read_client.get_wait("test")
190
- # time_c = time.time()
191
-
192
- # write_time = int((time_b - time_a) * 1e6)
193
- # read_time = int((time_c - time_b) * 1e6)
194
- # print(f"write time: {write_time} us, read time: {read_time} us")
195
226
 
196
227
  assert msg == "test_msg"
197
228
 
@@ -221,15 +252,8 @@ def test_speed_reading():
221
252
  p_speed_read = mp.Process(target=speed_read, args=(stop_flag,))
222
253
  p_speed_read.start()
223
254
 
224
- # time_a = time.time()
225
255
  write_client.put("test", "test_msg")
226
- # time_b = time.time()
227
256
  msg = read_client.get_wait("test")
228
- # time_c = time.time()
229
-
230
- # write_time = int((time_b - time_a) * 1e6)
231
- # read_time = int((time_c - time_b) * 1e6)
232
- # print(f"write time: {write_time} us, read time: {read_time} us")
233
257
 
234
258
  assert msg == "test_msg"
235
259
 
@@ -238,5 +262,8 @@ def test_speed_reading():
238
262
  p_speed_read.kill()
239
263
  read_client.close()
240
264
  write_client.shutdown_server()
241
- # time.sleep(_pause)
242
265
  write_client.close()
266
+
267
+
268
+ if __name__ == "__main__":
269
+ test_get_latest()
@@ -1,4 +1,13 @@
1
1
  import multiprocessing as mp
2
+
3
+ # spawn is the default method on macOS,
4
+ # starting in Python 3.14 it will be the default in Linux too.
5
+ try:
6
+ mp.set_start_method("spawn")
7
+ except RuntimeError as e:
8
+ print(e)
9
+ print(f"Using multiprocessing start method: {mp.get_start_method()}")
10
+
2
11
  import time
3
12
 
4
13
  from dsmq.server import serve
@@ -11,8 +20,8 @@ verbose = False
11
20
  _pause = 0.01
12
21
  _very_long_pause = 1.0
13
22
 
14
- _n_iter = int(1e3)
15
- _n_long_char = int(1e4)
23
+ _n_iter = int(1e4)
24
+ _n_long_char = int(1e5)
16
25
 
17
26
  _short_msg = "q"
18
27
  _long_msg = str(["q"] * _n_long_char)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dsmq
3
- Version: 1.3.0
3
+ Version: 1.4.0
4
4
  Summary: A dead simple message queue
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.10
@@ -152,6 +152,16 @@ connected to the server.
152
152
  in the topic, or the topic doesn't yet exist,
153
153
  returns `""`.
154
154
 
155
+ ### `get_latest(topic)`
156
+
157
+ Get the *most recent* eligible message from the queue named `topic`.
158
+ All the messages older than that in the queue become ineligible and never
159
+ get seen by the client.
160
+ - `topic` (str)
161
+ - returns str, the content of the message. If there was no eligble message
162
+ in the topic, or the topic doesn't yet exist,
163
+ returns `""`.
164
+
155
165
  ### `get_wait(topic)`
156
166
 
157
167
  A variant of `get()` that retries a few times until it gets
@@ -0,0 +1,15 @@
1
+ dsmq/.client.py.swp,sha256=bm5ybp3ESthgsOMpHRygMeQWbSlK4xw8toIKiT5A0pM,12288
2
+ dsmq/.server.py.swp,sha256=R61qwjXxZUZAbNpEXSW_jvxkXvy4qqt7gA1FEzwE-jk,20480
3
+ dsmq/__init__.py,sha256=YCgbnQAk8YbtHRyMcU0v2O7RdRhPhlT-vS_q40a7Q6g,50
4
+ dsmq/client.py,sha256=a4_6jcF7RAC58And0OlR3vL42FgZ24q2BECS-VF4Ri4,3438
5
+ dsmq/demo.py,sha256=x2ueymZnSQNtQ-HjzRiSCa0rg4NOm0YUIaj5VPXfnD8,718
6
+ dsmq/example_get_client.py,sha256=PvAsDGEAH1kVBifLVg2rx8ZxnAZmvzVCvZq13VgpLds,301
7
+ dsmq/example_put_client.py,sha256=QxDc3i7KAjjhpwxRRpI0Ke5KTNSPuBf9kkcGyTvUEaw,353
8
+ dsmq/server.py,sha256=yANPboqqoAw7GnSyHs_SeYlZjKkQPDMIhRo7tGDw6zo,10041
9
+ dsmq/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ dsmq/tests/integration_test.py,sha256=zF6v_3UHt5KqveX22gEpCSLcoeOyEWLSy_gMLoytg2U,6303
11
+ dsmq/tests/performance_suite.py,sha256=LgNpqD0zumEOgrNDLkeirBmQAALhlxEH7bXe4z-EuIY,5474
12
+ dsmq-1.4.0.dist-info/METADATA,sha256=RTvtHRtUg4HSOvyMRTu7sFy3uu3Ddd7iOXD1IWK91QQ,5203
13
+ dsmq-1.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
+ dsmq-1.4.0.dist-info/licenses/LICENSE,sha256=3Yu1mAp5VsKmnDtzkiOY7BdmrLeNwwZ3t6iWaLnlL0Y,1071
15
+ dsmq-1.4.0.dist-info/RECORD,,
Binary file
@@ -1,15 +0,0 @@
1
- dsmq/.server.py.swp,sha256=RLjzmw9FyOQY73HurM9PEjp4meAgVEZIN8M92OqgBzw,28672
2
- dsmq/__init__.py,sha256=YCgbnQAk8YbtHRyMcU0v2O7RdRhPhlT-vS_q40a7Q6g,50
3
- dsmq/client.py,sha256=_JJlDFTw1-VsRNsm-grYvexrWkkh50n62Q6-aGWum5Q,2547
4
- dsmq/demo.py,sha256=K53cC5kN7K4kNJlPq7c5OTIMHRCKTo9hYX2aIos57rU,542
5
- dsmq/example_get_client.py,sha256=PvAsDGEAH1kVBifLVg2rx8ZxnAZmvzVCvZq13VgpLds,301
6
- dsmq/example_put_client.py,sha256=QxDc3i7KAjjhpwxRRpI0Ke5KTNSPuBf9kkcGyTvUEaw,353
7
- dsmq/server.py,sha256=Ej6iQ1aw4LVHyHvdeZxxwSZS3VBIQP0qsFstuo0hwnY,8487
8
- dsmq/tests/.performance_suite.py.swp,sha256=D3B86JpgBIYDE0at6nG2Uw9WWASFxp12mB9zlNXYPbA,24576
9
- dsmq/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- dsmq/tests/integration_test.py,sha256=dLsQGCmpXv4zRb93TriccH7TbUyD9MHcLckAQqfDOK4,5980
11
- dsmq/tests/performance_suite.py,sha256=E59zB2ZvM8V5f8RxaB7p-Kehqyhrgsl0sXuy7g74BaI,5218
12
- dsmq-1.3.0.dist-info/METADATA,sha256=bZRJ0Oz5Vnavl4AU9bO2f7TuktSpUOJQ9hyx7Yusne4,4859
13
- dsmq-1.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
- dsmq-1.3.0.dist-info/licenses/LICENSE,sha256=3Yu1mAp5VsKmnDtzkiOY7BdmrLeNwwZ3t6iWaLnlL0Y,1071
15
- dsmq-1.3.0.dist-info/RECORD,,
File without changes