dsmq 1.2.3__py3-none-any.whl → 1.3.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/.server.py.swp +0 -0
- dsmq/client.py +5 -0
- dsmq/server.py +149 -110
- dsmq/tests/.performance_suite.py.swp +0 -0
- dsmq/tests/integration_test.py +0 -1
- dsmq/tests/performance_suite.py +179 -0
- {dsmq-1.2.3.dist-info → dsmq-1.3.0.dist-info}/METADATA +6 -1
- dsmq-1.3.0.dist-info/RECORD +15 -0
- dsmq-1.2.3.dist-info/RECORD +0 -12
- {dsmq-1.2.3.dist-info → dsmq-1.3.0.dist-info}/WHEEL +0 -0
- {dsmq-1.2.3.dist-info → dsmq-1.3.0.dist-info}/licenses/LICENSE +0 -0
dsmq/.server.py.swp
ADDED
Binary file
|
dsmq/client.py
CHANGED
@@ -8,6 +8,7 @@ _default_port = 30008
|
|
8
8
|
|
9
9
|
_n_retries = 10
|
10
10
|
_initial_retry = 0.01 # seconds
|
11
|
+
_shutdown_delay = 0.1 # seconds
|
11
12
|
|
12
13
|
|
13
14
|
def connect(host=_default_host, port=_default_port, verbose=False):
|
@@ -68,6 +69,10 @@ class DSMQClientSideConnection:
|
|
68
69
|
def shutdown_server(self):
|
69
70
|
msg_dict = {"action": "shutdown", "topic": ""}
|
70
71
|
self.websocket.send(json.dumps(msg_dict))
|
72
|
+
# Give the server time to wind down
|
73
|
+
time.sleep(_shutdown_delay)
|
71
74
|
|
72
75
|
def close(self):
|
73
76
|
self.websocket.close()
|
77
|
+
# Give the websocket time to wind down
|
78
|
+
time.sleep(_shutdown_delay)
|
dsmq/server.py
CHANGED
@@ -5,16 +5,20 @@ import sys
|
|
5
5
|
from threading import Thread
|
6
6
|
import time
|
7
7
|
from websockets.sync.server import serve as ws_serve
|
8
|
-
from websockets.exceptions import ConnectionClosedError
|
8
|
+
from websockets.exceptions import ConnectionClosedError, ConnectionClosedOK
|
9
9
|
|
10
10
|
_default_host = "127.0.0.1"
|
11
11
|
_default_port = 30008
|
12
|
-
_n_retries =
|
13
|
-
_first_retry = 0.
|
14
|
-
|
15
|
-
_time_to_live = 600.0 # seconds
|
12
|
+
_n_retries = 20
|
13
|
+
_first_retry = 0.005 # seconds
|
14
|
+
_time_to_live = 60.0 # seconds
|
16
15
|
|
16
|
+
# _db_name = ":memory:"
|
17
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."
|
18
22
|
|
19
23
|
# Make this global so it's easy to share
|
20
24
|
dsmq_server = None
|
@@ -40,6 +44,14 @@ def serve(host=_default_host, port=_default_port, verbose=False):
|
|
40
44
|
|
41
45
|
sqlite_conn = sqlite3.connect(_db_name)
|
42
46
|
cursor = sqlite_conn.cursor()
|
47
|
+
|
48
|
+
# Tweak the connection to make it faster
|
49
|
+
# 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")
|
54
|
+
|
43
55
|
cursor.execute("""
|
44
56
|
CREATE TABLE IF NOT EXISTS messages (timestamp DOUBLE, topic TEXT, message TEXT)
|
45
57
|
""")
|
@@ -52,12 +64,33 @@ CREATE TABLE IF NOT EXISTS messages (timestamp DOUBLE, topic TEXT, message TEXT)
|
|
52
64
|
global dsmq_server
|
53
65
|
|
54
66
|
# dsmq_server = ws_serve(request_handler, host, port)
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
67
|
+
for i_retry in range(_n_retries):
|
68
|
+
try:
|
69
|
+
with ws_serve(request_handler, host, port) as dsmq_server:
|
70
|
+
dsmq_server.serve_forever()
|
71
|
+
|
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)
|
61
94
|
|
62
95
|
sqlite_conn.close()
|
63
96
|
|
@@ -70,114 +103,120 @@ def request_handler(websocket):
|
|
70
103
|
last_read_times = {}
|
71
104
|
time_of_last_purge = time.time()
|
72
105
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
msg["
|
106
|
+
try:
|
107
|
+
for msg_text in websocket:
|
108
|
+
msg = json.loads(msg_text)
|
109
|
+
topic = msg["topic"]
|
110
|
+
timestamp = time.time()
|
111
|
+
|
112
|
+
if msg["action"] == "put":
|
113
|
+
msg["timestamp"] = timestamp
|
114
|
+
|
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":
|
134
|
+
try:
|
135
|
+
last_read_time = last_read_times[topic]
|
136
|
+
except KeyError:
|
137
|
+
last_read_times[topic] = client_creation_time
|
138
|
+
last_read_time = last_read_times[topic]
|
139
|
+
msg["last_read_time"] = last_read_time
|
140
|
+
|
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
|
80
166
|
|
81
|
-
# This block allows for multiple retries if the database
|
82
|
-
# is busy.
|
83
|
-
for i_retry in range(_n_retries):
|
84
167
|
try:
|
85
|
-
cursor.
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
sqlite_conn.commit()
|
93
|
-
except sqlite3.OperationalError:
|
94
|
-
wait_time = _first_retry * 2**i_retry
|
95
|
-
time.sleep(wait_time)
|
96
|
-
continue
|
97
|
-
break
|
168
|
+
result = cursor.fetchall()[0]
|
169
|
+
message = result[0]
|
170
|
+
timestamp = result[1]
|
171
|
+
last_read_times[topic] = timestamp
|
172
|
+
except IndexError:
|
173
|
+
# Handle the case where no results are returned
|
174
|
+
message = ""
|
98
175
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
176
|
+
websocket.send(json.dumps({"message": message}))
|
177
|
+
elif msg["action"] == "shutdown":
|
178
|
+
# Run this from a separate thread to prevent deadlock
|
179
|
+
global dsmq_server
|
180
|
+
|
181
|
+
def shutdown_gracefully(server_to_shutdown):
|
182
|
+
server_to_shutdown.shutdown()
|
183
|
+
|
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
|
+
Thread(target=shutdown_gracefully, args=(dsmq_server,)).start()
|
193
|
+
break
|
194
|
+
else:
|
195
|
+
raise RuntimeWarning(
|
196
|
+
"dsmq client action must either be 'put', 'get', or 'shutdown'"
|
197
|
+
)
|
198
|
+
|
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:
|
110
203
|
try:
|
111
204
|
cursor.execute(
|
112
205
|
"""
|
113
|
-
|
114
|
-
timestamp
|
115
|
-
FROM messages,
|
116
|
-
(
|
117
|
-
SELECT MIN(timestamp) AS min_time
|
118
|
-
FROM messages
|
119
|
-
WHERE topic = :topic
|
120
|
-
AND timestamp > :last_read_time
|
121
|
-
) a
|
122
|
-
WHERE topic = :topic
|
123
|
-
AND timestamp = a.min_time
|
206
|
+
DELETE FROM messages
|
207
|
+
WHERE timestamp < :time_threshold
|
124
208
|
""",
|
125
|
-
|
209
|
+
{"time_threshold": time_of_last_purge},
|
126
210
|
)
|
211
|
+
sqlite_conn.commit()
|
212
|
+
time_of_last_purge = time.time()
|
127
213
|
except sqlite3.OperationalError:
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
result = cursor.fetchall()[0]
|
135
|
-
message = result[0]
|
136
|
-
timestamp = result[1]
|
137
|
-
last_read_times[topic] = timestamp
|
138
|
-
except IndexError:
|
139
|
-
# Handle the case where no results are returned
|
140
|
-
message = ""
|
141
|
-
|
142
|
-
try:
|
143
|
-
websocket.send(json.dumps({"message": message}))
|
144
|
-
except ConnectionClosedError:
|
145
|
-
pass
|
146
|
-
elif msg["action"] == "shutdown":
|
147
|
-
# Run this from a separate thread to prevent deadlock
|
148
|
-
global dsmq_server
|
149
|
-
|
150
|
-
def shutdown_gracefully(server_to_shutdown):
|
151
|
-
server_to_shutdown.shutdown()
|
152
|
-
|
153
|
-
filenames = os.listdir()
|
154
|
-
for filename in filenames:
|
155
|
-
if filename[: len(_db_name)] == _db_name:
|
156
|
-
try:
|
157
|
-
os.remove(filename)
|
158
|
-
except FileNotFoundError:
|
159
|
-
pass
|
160
|
-
|
161
|
-
Thread(target=shutdown_gracefully, args=(dsmq_server,)).start()
|
162
|
-
break
|
163
|
-
else:
|
164
|
-
raise RuntimeWarning(
|
165
|
-
"dsmq client action must either be 'put', 'get', or 'shutdown'"
|
166
|
-
)
|
167
|
-
|
168
|
-
# Periodically clean out messages from the queue that are
|
169
|
-
# past their sell buy date.
|
170
|
-
# This operation is pretty fast. I clock it at 12 us on my machine.
|
171
|
-
if time.time() - time_of_last_purge > _time_to_live:
|
172
|
-
cursor.execute(
|
173
|
-
"""
|
174
|
-
DELETE FROM messages
|
175
|
-
WHERE timestamp < :time_threshold
|
176
|
-
""",
|
177
|
-
{"time_threshold": time_of_last_purge},
|
178
|
-
)
|
179
|
-
sqlite_conn.commit()
|
180
|
-
time_of_last_purge = time.time()
|
214
|
+
# Database may be locked. Try again next time.
|
215
|
+
pass
|
216
|
+
except (ConnectionClosedError, ConnectionClosedOK):
|
217
|
+
# Something happened on the other end and this handler
|
218
|
+
# is no longer needed.
|
219
|
+
pass
|
181
220
|
|
182
221
|
sqlite_conn.close()
|
183
222
|
|
Binary file
|
dsmq/tests/integration_test.py
CHANGED
@@ -0,0 +1,179 @@
|
|
1
|
+
import multiprocessing as mp
|
2
|
+
import time
|
3
|
+
|
4
|
+
from dsmq.server import serve
|
5
|
+
from dsmq.client import connect
|
6
|
+
|
7
|
+
host = "127.0.0.1"
|
8
|
+
port = 30303
|
9
|
+
verbose = False
|
10
|
+
|
11
|
+
_pause = 0.01
|
12
|
+
_very_long_pause = 1.0
|
13
|
+
|
14
|
+
_n_iter = int(1e3)
|
15
|
+
_n_long_char = int(1e4)
|
16
|
+
|
17
|
+
_short_msg = "q"
|
18
|
+
_long_msg = str(["q"] * _n_long_char)
|
19
|
+
|
20
|
+
_test_topic = "test"
|
21
|
+
|
22
|
+
|
23
|
+
def main():
|
24
|
+
print()
|
25
|
+
print("dsmq timing measurements")
|
26
|
+
|
27
|
+
time_short_writes()
|
28
|
+
time_long_writes()
|
29
|
+
time_empty_reads()
|
30
|
+
time_short_reads()
|
31
|
+
time_long_reads()
|
32
|
+
|
33
|
+
|
34
|
+
def time_short_writes():
|
35
|
+
condition = "short write"
|
36
|
+
|
37
|
+
duration, duration_close = time_writes(msg=_short_msg, n_iter=1)
|
38
|
+
|
39
|
+
print()
|
40
|
+
print(f"Time for first {condition} [including closing]")
|
41
|
+
print(f" {int(duration)} μs [{int(duration_close)} μs]")
|
42
|
+
|
43
|
+
avg_duration, avg_duration_close = time_writes(msg=_short_msg, n_iter=_n_iter)
|
44
|
+
|
45
|
+
print(f"Average time for a {condition} [including closing]")
|
46
|
+
print(f" {int(avg_duration)} μs [{int(avg_duration_close)} μs]")
|
47
|
+
|
48
|
+
|
49
|
+
def time_long_writes():
|
50
|
+
duration, duration_close = time_writes(msg=_long_msg, n_iter=1)
|
51
|
+
|
52
|
+
condition = "long write"
|
53
|
+
print()
|
54
|
+
print(f"Time for first {condition} [including closing]")
|
55
|
+
print(f" {int(duration)} μs [{int(duration_close)} μs]")
|
56
|
+
|
57
|
+
avg_duration, avg_duration_close = time_writes(msg=_long_msg, n_iter=_n_iter)
|
58
|
+
|
59
|
+
condition = f"long write ({_n_long_char} characters)"
|
60
|
+
print(f"Average time for a {condition} [including closing]")
|
61
|
+
print(f" {int(avg_duration)} μs [{int(avg_duration_close)} μs]")
|
62
|
+
|
63
|
+
condition = "long write (per 1000 characters)"
|
64
|
+
print(f"Average time for a {condition} [including closing]")
|
65
|
+
print(
|
66
|
+
f" {int(1000 * avg_duration / _n_long_char)} μs "
|
67
|
+
+ f"[{int(1000 * avg_duration_close / _n_long_char)}] μs"
|
68
|
+
)
|
69
|
+
|
70
|
+
|
71
|
+
def time_writes(msg="message", n_iter=1):
|
72
|
+
p_server = mp.Process(target=serve, args=(host, port, verbose))
|
73
|
+
p_server.start()
|
74
|
+
time.sleep(_pause)
|
75
|
+
write_client = connect(host, port)
|
76
|
+
|
77
|
+
start_time = time.time()
|
78
|
+
for _ in range(n_iter):
|
79
|
+
write_client.put(_test_topic, msg)
|
80
|
+
avg_duration = 1e6 * (time.time() - start_time) / n_iter # microseconds
|
81
|
+
|
82
|
+
write_client.shutdown_server()
|
83
|
+
write_client.close()
|
84
|
+
|
85
|
+
p_server.join(_very_long_pause)
|
86
|
+
if p_server.is_alive():
|
87
|
+
print(" Doing a hard shutdown on mq server")
|
88
|
+
p_server.kill()
|
89
|
+
avg_duration_close = 1e6 * (time.time() - start_time) / n_iter # microseconds
|
90
|
+
|
91
|
+
return avg_duration, avg_duration_close
|
92
|
+
|
93
|
+
|
94
|
+
def time_empty_reads():
|
95
|
+
condition = "empty read"
|
96
|
+
|
97
|
+
duration, duration_close = time_reads(msg=None, n_iter=1)
|
98
|
+
|
99
|
+
print()
|
100
|
+
print(f"Time for first {condition} [including closing]")
|
101
|
+
print(f" {int(duration)} μs [{int(duration_close)} μs]")
|
102
|
+
|
103
|
+
avg_duration, avg_duration_close = time_reads(msg=None, n_iter=_n_iter)
|
104
|
+
|
105
|
+
print(f"Average time for a {condition} [including closing]")
|
106
|
+
print(f" {int(avg_duration)} μs [{int(avg_duration_close)} μs]")
|
107
|
+
|
108
|
+
|
109
|
+
def time_short_reads():
|
110
|
+
condition = "short read"
|
111
|
+
|
112
|
+
duration, duration_close = time_reads(msg=_short_msg, n_iter=1)
|
113
|
+
|
114
|
+
print()
|
115
|
+
print(f"Time for first {condition} [including closing]")
|
116
|
+
print(f" {int(duration)} μs [{int(duration_close)} μs]")
|
117
|
+
|
118
|
+
avg_duration, avg_duration_close = time_reads(msg=_short_msg, n_iter=_n_iter)
|
119
|
+
|
120
|
+
print(f"Average time for a {condition} [including closing]")
|
121
|
+
print(f" {int(avg_duration)} μs [{int(avg_duration_close)} μs]")
|
122
|
+
|
123
|
+
|
124
|
+
def time_long_reads():
|
125
|
+
condition = f"long read ({_n_long_char} characters)"
|
126
|
+
|
127
|
+
duration, duration_close = time_reads(msg=_long_msg, n_iter=1)
|
128
|
+
|
129
|
+
print()
|
130
|
+
print(f"Time for first {condition} [including closing]")
|
131
|
+
print(f" {int(duration)} μs [{int(duration_close)} μs]")
|
132
|
+
|
133
|
+
avg_duration, avg_duration_close = time_reads(msg=_long_msg, n_iter=_n_iter)
|
134
|
+
|
135
|
+
print(f"Average time for a {condition} [including closing]")
|
136
|
+
print(f" {int(avg_duration)} μs [{int(avg_duration_close)} μs]")
|
137
|
+
|
138
|
+
condition = "long read (per 1000 characters)"
|
139
|
+
print(f"Average time for a {condition} [including closing]")
|
140
|
+
print(
|
141
|
+
f" {int(1000 * avg_duration / _n_long_char)} μs "
|
142
|
+
+ f"[{int(1000 * avg_duration_close / _n_long_char)}] μs"
|
143
|
+
)
|
144
|
+
|
145
|
+
|
146
|
+
def time_reads(msg=None, n_iter=1):
|
147
|
+
p_server = mp.Process(target=serve, args=(host, port, verbose))
|
148
|
+
p_server.start()
|
149
|
+
time.sleep(_pause)
|
150
|
+
# write_client = connect(host, port)
|
151
|
+
read_client = connect(host, port)
|
152
|
+
|
153
|
+
if msg is not None:
|
154
|
+
for _ in range(n_iter):
|
155
|
+
read_client.put(_test_topic, msg)
|
156
|
+
|
157
|
+
start_time = time.time()
|
158
|
+
for _ in range(n_iter):
|
159
|
+
msg = read_client.get(_test_topic)
|
160
|
+
|
161
|
+
avg_duration = 1e6 * (time.time() - start_time) / n_iter # microseconds
|
162
|
+
|
163
|
+
read_client.shutdown_server()
|
164
|
+
# write_client.close()
|
165
|
+
read_client.close()
|
166
|
+
|
167
|
+
p_server.join(_very_long_pause)
|
168
|
+
|
169
|
+
if p_server.is_alive():
|
170
|
+
print(" Doing a hard shutdown on mq server")
|
171
|
+
p_server.kill()
|
172
|
+
|
173
|
+
avg_duration_close = 1e6 * (time.time() - start_time) / n_iter # microseconds
|
174
|
+
|
175
|
+
return avg_duration, avg_duration_close
|
176
|
+
|
177
|
+
|
178
|
+
if __name__ == "__main__":
|
179
|
+
main()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: dsmq
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.3.0
|
4
4
|
Summary: A dead simple message queue
|
5
5
|
License-File: LICENSE
|
6
6
|
Requires-Python: >=3.10
|
@@ -178,3 +178,8 @@ Run all the tests in `src/dsmq/tests/` with pytest, for example
|
|
178
178
|
```
|
179
179
|
uv run pytest
|
180
180
|
```
|
181
|
+
|
182
|
+
# Performance characterization
|
183
|
+
|
184
|
+
Time typical operations on your system with the script at
|
185
|
+
`src/dsmq/tests/performance_suite.py`
|
@@ -0,0 +1,15 @@
|
|
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,,
|
dsmq-1.2.3.dist-info/RECORD
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
dsmq/__init__.py,sha256=YCgbnQAk8YbtHRyMcU0v2O7RdRhPhlT-vS_q40a7Q6g,50
|
2
|
-
dsmq/client.py,sha256=p6irQZOE4b2fpTUwSrEUraPmrJzvT8QSU01ak9qpGCQ,2351
|
3
|
-
dsmq/demo.py,sha256=K53cC5kN7K4kNJlPq7c5OTIMHRCKTo9hYX2aIos57rU,542
|
4
|
-
dsmq/example_get_client.py,sha256=PvAsDGEAH1kVBifLVg2rx8ZxnAZmvzVCvZq13VgpLds,301
|
5
|
-
dsmq/example_put_client.py,sha256=QxDc3i7KAjjhpwxRRpI0Ke5KTNSPuBf9kkcGyTvUEaw,353
|
6
|
-
dsmq/server.py,sha256=HkV1yTYe0u_P6TZVAS_wBkT6TDLySfmdZPVvt-VyeF0,6219
|
7
|
-
dsmq/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
-
dsmq/tests/integration_test.py,sha256=lC97LAzdQixv75OwjqjKTvYnSZpsP0zuzFP8ocUnjl8,6031
|
9
|
-
dsmq-1.2.3.dist-info/METADATA,sha256=H3t1g1Hs68VsheHaCqzrp2bs1ekwzUM5ajC7bzgjH34,4730
|
10
|
-
dsmq-1.2.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
11
|
-
dsmq-1.2.3.dist-info/licenses/LICENSE,sha256=3Yu1mAp5VsKmnDtzkiOY7BdmrLeNwwZ3t6iWaLnlL0Y,1071
|
12
|
-
dsmq-1.2.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|