tina4-python 0.2.122__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.
- tina4_python/Auth.py +222 -0
- tina4_python/Constant.py +43 -0
- tina4_python/Database.py +591 -0
- tina4_python/DatabaseResult.py +107 -0
- tina4_python/DatabaseTypes.py +15 -0
- tina4_python/Debug.py +126 -0
- tina4_python/Env.py +37 -0
- tina4_python/Localization.py +42 -0
- tina4_python/Messages.py +30 -0
- tina4_python/MiddleWare.py +90 -0
- tina4_python/Migration.py +107 -0
- tina4_python/ORM.py +639 -0
- tina4_python/Queue.py +615 -0
- tina4_python/Request.py +19 -0
- tina4_python/Response.py +121 -0
- tina4_python/Router.py +423 -0
- tina4_python/Session.py +342 -0
- tina4_python/ShellColors.py +20 -0
- tina4_python/Swagger.py +228 -0
- tina4_python/Template.py +107 -0
- tina4_python/Webserver.py +429 -0
- tina4_python/Websocket.py +49 -0
- tina4_python/__init__.py +392 -0
- tina4_python/messages.pot +83 -0
- tina4_python/public/css/readme.md +0 -0
- tina4_python/public/favicon.ico +0 -0
- tina4_python/public/images/403.png +0 -0
- tina4_python/public/images/404.png +0 -0
- tina4_python/public/images/500.png +0 -0
- tina4_python/public/images/logo.png +0 -0
- tina4_python/public/images/readme.md +0 -0
- tina4_python/public/js/readme.md +0 -0
- tina4_python/public/js/reconnecting-websocket.js +365 -0
- tina4_python/public/js/tina4helper.js +397 -0
- tina4_python/public/swagger/index.html +90 -0
- tina4_python/public/swagger/oauth2-redirect.html +63 -0
- tina4_python/templates/errors/403.twig +10 -0
- tina4_python/templates/errors/404.twig +10 -0
- tina4_python/templates/errors/500.twig +11 -0
- tina4_python/templates/readme.md +1 -0
- tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
- tina4_python/translations/en/LC_MESSAGES/messages.po +80 -0
- tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
- tina4_python/translations/fr/LC_MESSAGES/messages.po +84 -0
- tina4_python-0.2.122.dist-info/METADATA +465 -0
- tina4_python-0.2.122.dist-info/RECORD +47 -0
- tina4_python-0.2.122.dist-info/WHEEL +4 -0
tina4_python/Queue.py
ADDED
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Tina4 - This is not a 4ramework.
|
|
3
|
+
# Copy-right 2007 - current Tina4
|
|
4
|
+
# License: MIT https://opensource.org/licenses/MIT
|
|
5
|
+
#
|
|
6
|
+
# flake8: noqa: E501
|
|
7
|
+
import json
|
|
8
|
+
import sys
|
|
9
|
+
import os
|
|
10
|
+
import importlib
|
|
11
|
+
import time
|
|
12
|
+
from tina4_python import Debug
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from typing import Dict, Any
|
|
15
|
+
|
|
16
|
+
_DKW: Dict[str, Any] = {}
|
|
17
|
+
if sys.version_info >= (3, 10):
|
|
18
|
+
_DKW["slots"] = True
|
|
19
|
+
|
|
20
|
+
# Extracted from https://github.com/stevesimmons/uuid7 under MIT license
|
|
21
|
+
|
|
22
|
+
# Expose function used by uuid7() to get current time in nanoseconds
|
|
23
|
+
# since the Unix epoch.
|
|
24
|
+
time_ns = time.time_ns
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def uuid7(
|
|
28
|
+
_last=[0, 0, 0, 0], # noqa
|
|
29
|
+
_last_as_of=[0, 0, 0, 0], # noqa
|
|
30
|
+
) -> str:
|
|
31
|
+
"""
|
|
32
|
+
UUID v7, following the proposed extension to RFC4122 described in
|
|
33
|
+
https://www.ietf.org/id/draft-peabody-dispatch-new-uuid-format-02.html.
|
|
34
|
+
All representations sort chronologically, with a potential time resolution
|
|
35
|
+
of 50ns (if the system clock supports this).
|
|
36
|
+
Parameters
|
|
37
|
+
----------
|
|
38
|
+
time_func - Set the time function, which must return integer
|
|
39
|
+
nanoseconds since the Unix epoch, midnight on 1-Jan-1970.
|
|
40
|
+
Defaults to time.time_ns(). This is exposed because
|
|
41
|
+
time.time_ns() may have a low resolution on Windows.
|
|
42
|
+
_last and _last_as_of - Used internally to trigger incrementing a
|
|
43
|
+
sequence counter when consecutive calls have the same time
|
|
44
|
+
values. The values [t1, t2, t3, seq] are described below.
|
|
45
|
+
Returns
|
|
46
|
+
-------
|
|
47
|
+
A UUID object, or if as_type is specified, a string, int or
|
|
48
|
+
bytes of length 16.
|
|
49
|
+
Implementation notes
|
|
50
|
+
--------------------
|
|
51
|
+
The 128 bits in the UUID are allocated as follows:
|
|
52
|
+
- 36 bits of whole seconds
|
|
53
|
+
- 24 bits of fractional seconds, giving approx 50ns resolution
|
|
54
|
+
- 14 bits of sequential counter, if called repeatedly in same time tick
|
|
55
|
+
- 48 bits of randomness
|
|
56
|
+
plus, at locations defined by RFC4122, 4 bits for the
|
|
57
|
+
uuid version (0b111) and 2 bits for the uuid variant (0b10).
|
|
58
|
+
0 1 2 3
|
|
59
|
+
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
60
|
+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
61
|
+
t1 | unixts (secs since epoch) |
|
|
62
|
+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
63
|
+
t2/t3 |unixts | frac secs (12 bits) | ver | frac secs (12 bits) |
|
|
64
|
+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
65
|
+
t4/rand |var| seq (14 bits) | rand (16 bits) |
|
|
66
|
+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
67
|
+
rand | rand (32 bits) |
|
|
68
|
+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
69
|
+
Indicative timings:
|
|
70
|
+
- uuid.uuid4() 2.4us
|
|
71
|
+
- uuid7(as_type='str') 2.5us
|
|
72
|
+
Examples
|
|
73
|
+
--------
|
|
74
|
+
>>> uuid7()
|
|
75
|
+
'061cb26a-54b8-7a52-8000-2124e7041024'
|
|
76
|
+
>>> uuid7(0)
|
|
77
|
+
'00000000-0000-0000-0000-00000000000'
|
|
78
|
+
"""
|
|
79
|
+
ns = time_ns()
|
|
80
|
+
last = _last
|
|
81
|
+
|
|
82
|
+
if ns == 0:
|
|
83
|
+
# Special case for all-zero uuid. Strictly speaking not a UUIDv7.
|
|
84
|
+
t1 = t2 = t3 = t4 = 0
|
|
85
|
+
rand = b"\0" * 6
|
|
86
|
+
else:
|
|
87
|
+
# Treat the first 8 bytes of the uuid as a long (t1) and two ints
|
|
88
|
+
# (t2 and t3) holding 36 bits of whole seconds and 24 bits of
|
|
89
|
+
# fractional seconds.
|
|
90
|
+
# This gives a nominal 60ns resolution, comparable to the
|
|
91
|
+
# timestamp precision in Linux (~200ns) and Windows (100ns ticks).
|
|
92
|
+
sixteen_secs = 16_000_000_000
|
|
93
|
+
t1, rest1 = divmod(ns, sixteen_secs)
|
|
94
|
+
t2, rest2 = divmod(rest1 << 16, sixteen_secs)
|
|
95
|
+
t3, _ = divmod(rest2 << 12, sixteen_secs)
|
|
96
|
+
t3 |= 7 << 12 # Put uuid version in top 4 bits, which are 0 in t3
|
|
97
|
+
|
|
98
|
+
# The next two bytes are an int (t4) with two bits for
|
|
99
|
+
# the variant 2 and a 14 bit sequence counter which increments
|
|
100
|
+
# if the time is unchanged.
|
|
101
|
+
if t1 == last[0] and t2 == last[1] and t3 == last[2]:
|
|
102
|
+
# Stop the seq counter wrapping past 0x3FFF.
|
|
103
|
+
# This won't happen in practice, but if it does,
|
|
104
|
+
# uuids after the 16383rd with that same timestamp
|
|
105
|
+
# will not longer be correctly ordered but
|
|
106
|
+
# are still unique due to the 6 random bytes.
|
|
107
|
+
if last[3] < 0x3FFF:
|
|
108
|
+
last[3] += 1
|
|
109
|
+
else:
|
|
110
|
+
last[:] = (t1, t2, t3, 0)
|
|
111
|
+
t4 = (2 << 14) | last[3] # Put variant 0b10 in top two bits
|
|
112
|
+
|
|
113
|
+
# Six random bytes for the lower part of the uuid
|
|
114
|
+
rand = os.urandom(6)
|
|
115
|
+
|
|
116
|
+
return f"{t1:>08x}-{t2:>04x}-{t3:>04x}-{t4:>04x}-{rand.hex()}"
|
|
117
|
+
|
|
118
|
+
class Config(object):
|
|
119
|
+
queue_type = "litequeue" # can be rabbitmq or kafka as well
|
|
120
|
+
litequeue_database_name = "queue.db"
|
|
121
|
+
kafka_config = None
|
|
122
|
+
rabbitmq_config = None
|
|
123
|
+
mongo_queue_config = None
|
|
124
|
+
rabbitmq_queue = "default-queue"
|
|
125
|
+
prefix=""
|
|
126
|
+
|
|
127
|
+
def __init__(self):
|
|
128
|
+
pass
|
|
129
|
+
|
|
130
|
+
@dataclass(frozen=True, **_DKW)
|
|
131
|
+
class Message:
|
|
132
|
+
message_id: str
|
|
133
|
+
data: str
|
|
134
|
+
user_id: str
|
|
135
|
+
status: int
|
|
136
|
+
time_stamp: int
|
|
137
|
+
delivery_tag: str
|
|
138
|
+
|
|
139
|
+
class Queue(object):
|
|
140
|
+
def __init__(self, config=None, topic="default-queue", callback=None):
|
|
141
|
+
"""
|
|
142
|
+
Initializes the queue object
|
|
143
|
+
:param config:
|
|
144
|
+
:param topic:
|
|
145
|
+
:param callback:
|
|
146
|
+
"""
|
|
147
|
+
if config is None:
|
|
148
|
+
config = Config()
|
|
149
|
+
config.queue_type = "litequeue"
|
|
150
|
+
config.litequeue_database_name = "queue.db"
|
|
151
|
+
config.prefix = ""
|
|
152
|
+
|
|
153
|
+
Debug.info("Initializing", config.queue_type, topic)
|
|
154
|
+
self.producer = None
|
|
155
|
+
self.consumer = None
|
|
156
|
+
self.topic = topic
|
|
157
|
+
self.config = config
|
|
158
|
+
self.library = None
|
|
159
|
+
self.callback = callback
|
|
160
|
+
|
|
161
|
+
if config.queue_type == "litequeue":
|
|
162
|
+
self.init_litequeue()
|
|
163
|
+
elif config.queue_type == "mongo-queue-service":
|
|
164
|
+
self.init_mongoqueue()
|
|
165
|
+
elif config.queue_type == "rabbitmq":
|
|
166
|
+
self.init_rabbitmq()
|
|
167
|
+
elif config.queue_type == "kafka":
|
|
168
|
+
self.init_kafka()
|
|
169
|
+
|
|
170
|
+
def produce(self, value, user_id=None, delivery_callback=None):
|
|
171
|
+
"""
|
|
172
|
+
Produces a message to the queue
|
|
173
|
+
:param value:
|
|
174
|
+
:param user_id:
|
|
175
|
+
:param delivery_callback:
|
|
176
|
+
:return:
|
|
177
|
+
"""
|
|
178
|
+
if self.config.prefix != "":
|
|
179
|
+
prefix = self.config.prefix+"_"
|
|
180
|
+
else:
|
|
181
|
+
prefix = ""
|
|
182
|
+
|
|
183
|
+
if self.config.queue_type == "litequeue":
|
|
184
|
+
try:
|
|
185
|
+
msg = self.producer.put(json.dumps({"msg": value, "user_id": user_id}))
|
|
186
|
+
response_msg = Message(
|
|
187
|
+
msg.message_id,
|
|
188
|
+
value,
|
|
189
|
+
user_id,
|
|
190
|
+
int(msg.status),
|
|
191
|
+
msg.in_time,
|
|
192
|
+
0
|
|
193
|
+
)
|
|
194
|
+
if delivery_callback is not None:
|
|
195
|
+
delivery_callback(self.producer, None, response_msg)
|
|
196
|
+
|
|
197
|
+
return response_msg
|
|
198
|
+
except Exception as e:
|
|
199
|
+
if delivery_callback is not None:
|
|
200
|
+
delivery_callback(self.producer, e, None)
|
|
201
|
+
|
|
202
|
+
return e
|
|
203
|
+
elif self.config.queue_type == "mongo-queue-service":
|
|
204
|
+
try:
|
|
205
|
+
body = {
|
|
206
|
+
"message_id": uuid7(), "msg": value, "user_id": user_id, "in_time": time_ns()
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
#queue.put({"task_id": 1}, priority=1, channel="channel_1", job_id="x_job")
|
|
210
|
+
self.producer.put(body, priority=1,channel=self.topic, job_id=body["message_id"])
|
|
211
|
+
response_msg = Message(
|
|
212
|
+
body["message_id"],
|
|
213
|
+
value,
|
|
214
|
+
user_id,
|
|
215
|
+
0,
|
|
216
|
+
body["in_time"],
|
|
217
|
+
0
|
|
218
|
+
)
|
|
219
|
+
if delivery_callback is not None:
|
|
220
|
+
delivery_callback(self.producer, None, response_msg)
|
|
221
|
+
|
|
222
|
+
return response_msg
|
|
223
|
+
except Exception as e:
|
|
224
|
+
if delivery_callback is not None:
|
|
225
|
+
delivery_callback(self.producer, e, None)
|
|
226
|
+
return e
|
|
227
|
+
elif self.config.queue_type == "rabbitmq":
|
|
228
|
+
try:
|
|
229
|
+
body = {
|
|
230
|
+
"message_id": uuid7(), "msg": value, "user_id": user_id, "in_time": time_ns()
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
self.producer.basic_publish(exchange=prefix+self.topic, routing_key='', body=json.dumps(body))
|
|
234
|
+
response_msg = Message(
|
|
235
|
+
body["message_id"],
|
|
236
|
+
value,
|
|
237
|
+
user_id,
|
|
238
|
+
0,
|
|
239
|
+
body["in_time"],
|
|
240
|
+
0
|
|
241
|
+
)
|
|
242
|
+
if delivery_callback is not None:
|
|
243
|
+
delivery_callback(self.producer, None, response_msg)
|
|
244
|
+
|
|
245
|
+
return response_msg
|
|
246
|
+
except Exception as e:
|
|
247
|
+
if delivery_callback is not None:
|
|
248
|
+
delivery_callback(self.producer, e, None)
|
|
249
|
+
return e
|
|
250
|
+
elif self.config.queue_type == "kafka":
|
|
251
|
+
try:
|
|
252
|
+
body = {
|
|
253
|
+
"message_id": uuid7(), "msg": value, "user_id": user_id, "in_time": time_ns()
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
response_msg = Message(
|
|
257
|
+
body["message_id"],
|
|
258
|
+
value,
|
|
259
|
+
user_id,
|
|
260
|
+
0,
|
|
261
|
+
body["in_time"],
|
|
262
|
+
0
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
def kafka_delivery_callback(err, kafka_msg):
|
|
266
|
+
if err:
|
|
267
|
+
delivery_callback(self.consumer, err, None)
|
|
268
|
+
else:
|
|
269
|
+
response_msg_internal = Message(
|
|
270
|
+
body["message_id"],
|
|
271
|
+
value,
|
|
272
|
+
user_id,
|
|
273
|
+
0,
|
|
274
|
+
body["in_time"],
|
|
275
|
+
kafka_msg.offset()
|
|
276
|
+
)
|
|
277
|
+
delivery_callback(self.consumer, err, response_msg_internal)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
self.producer.produce(prefix+self.topic, json.dumps(body), user_id, callback=kafka_delivery_callback)
|
|
281
|
+
self.producer.poll(1000)
|
|
282
|
+
self.producer.flush()
|
|
283
|
+
|
|
284
|
+
return response_msg
|
|
285
|
+
|
|
286
|
+
except Exception as e:
|
|
287
|
+
delivery_callback(self.consumer, e, None)
|
|
288
|
+
return e
|
|
289
|
+
pass
|
|
290
|
+
|
|
291
|
+
def consume(self, acknowledge=True, consumer_callback=None):
|
|
292
|
+
"""
|
|
293
|
+
Consumes a message from the queue
|
|
294
|
+
:param acknowledge:
|
|
295
|
+
:param consumer_callback:
|
|
296
|
+
:return:
|
|
297
|
+
"""
|
|
298
|
+
|
|
299
|
+
if self.config.prefix != "":
|
|
300
|
+
prefix = self.config.prefix+"_"
|
|
301
|
+
else:
|
|
302
|
+
prefix = ""
|
|
303
|
+
|
|
304
|
+
if self.config.queue_type == "litequeue":
|
|
305
|
+
try:
|
|
306
|
+
msg = self.consumer.pop()
|
|
307
|
+
if msg is not None:
|
|
308
|
+
data = json.loads(msg.data)
|
|
309
|
+
response_msg = Message(
|
|
310
|
+
msg.message_id,
|
|
311
|
+
data["msg"],
|
|
312
|
+
data["user_id"],
|
|
313
|
+
msg.status,
|
|
314
|
+
msg.in_time,
|
|
315
|
+
0
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
if consumer_callback is not None:
|
|
319
|
+
consumer_callback(self.consumer, None, response_msg)
|
|
320
|
+
|
|
321
|
+
if acknowledge:
|
|
322
|
+
self.consumer.done(msg.message_id)
|
|
323
|
+
msg = self.consumer.get(msg.message_id)
|
|
324
|
+
data = json.loads(msg.data)
|
|
325
|
+
response_msg = Message(
|
|
326
|
+
msg.message_id,
|
|
327
|
+
data["msg"],
|
|
328
|
+
data["user_id"],
|
|
329
|
+
msg.status,
|
|
330
|
+
msg.in_time,
|
|
331
|
+
0
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
if consumer_callback is not None:
|
|
335
|
+
consumer_callback(self.consumer, None, response_msg)
|
|
336
|
+
|
|
337
|
+
except Exception as e:
|
|
338
|
+
Debug.error("Error consuming ", self.topic, str(e))
|
|
339
|
+
elif self.config.queue_type == "mongo-queue-service":
|
|
340
|
+
try:
|
|
341
|
+
msg = self.consumer.next(channel=self.topic)
|
|
342
|
+
if msg is not None:
|
|
343
|
+
msg_status = 1
|
|
344
|
+
if acknowledge:
|
|
345
|
+
msg_status = 2
|
|
346
|
+
msg.complete()
|
|
347
|
+
data = msg.payload
|
|
348
|
+
response_msg = Message(
|
|
349
|
+
data["message_id"],
|
|
350
|
+
data["msg"],
|
|
351
|
+
data["user_id"],
|
|
352
|
+
msg_status,
|
|
353
|
+
msg.queued_at,
|
|
354
|
+
0
|
|
355
|
+
)
|
|
356
|
+
if consumer_callback is not None:
|
|
357
|
+
consumer_callback(msg, None, response_msg)
|
|
358
|
+
except Exception as e:
|
|
359
|
+
msg.error(str(e))
|
|
360
|
+
Debug.error("Error consuming ", self.topic, str(e))
|
|
361
|
+
elif self.config.queue_type == "rabbitmq":
|
|
362
|
+
try:
|
|
363
|
+
method_frame, header_frame, body = self.consumer.basic_get(queue=prefix+self.topic, auto_ack=acknowledge)
|
|
364
|
+
|
|
365
|
+
if method_frame is not None:
|
|
366
|
+
msg_status = 1
|
|
367
|
+
if acknowledge:
|
|
368
|
+
msg_status = 2
|
|
369
|
+
data = json.loads(body)
|
|
370
|
+
response_msg = Message(
|
|
371
|
+
data["message_id"],
|
|
372
|
+
data["msg"],
|
|
373
|
+
data["user_id"],
|
|
374
|
+
msg_status,
|
|
375
|
+
data["in_time"],
|
|
376
|
+
method_frame.delivery_tag
|
|
377
|
+
)
|
|
378
|
+
if consumer_callback is not None:
|
|
379
|
+
consumer_callback(self.consumer, None, response_msg)
|
|
380
|
+
except Exception as e:
|
|
381
|
+
Debug.error("Error consuming ", self.topic, str(e))
|
|
382
|
+
|
|
383
|
+
elif self.config.queue_type == "kafka":
|
|
384
|
+
try:
|
|
385
|
+
msg = self.consumer.poll(1.0)
|
|
386
|
+
if msg is None:
|
|
387
|
+
pass
|
|
388
|
+
elif msg.error():
|
|
389
|
+
if consumer_callback is not None:
|
|
390
|
+
consumer_callback(self.consumer, msg.error(), None)
|
|
391
|
+
else:
|
|
392
|
+
msg_status = 1
|
|
393
|
+
if acknowledge:
|
|
394
|
+
msg_status = 2
|
|
395
|
+
data = json.loads(msg.value().decode('utf-8'))
|
|
396
|
+
response_msg = Message(
|
|
397
|
+
data["message_id"],
|
|
398
|
+
data["msg"],
|
|
399
|
+
data["user_id"],
|
|
400
|
+
msg_status,
|
|
401
|
+
data["in_time"],
|
|
402
|
+
msg.offset()
|
|
403
|
+
)
|
|
404
|
+
if consumer_callback is not None:
|
|
405
|
+
consumer_callback(self.consumer, None, response_msg)
|
|
406
|
+
except Exception as e:
|
|
407
|
+
Debug.error("Error consuming ", self.topic, str(e))
|
|
408
|
+
|
|
409
|
+
def init_litequeue(self):
|
|
410
|
+
"""
|
|
411
|
+
Initializes lite queue
|
|
412
|
+
:return:
|
|
413
|
+
"""
|
|
414
|
+
try:
|
|
415
|
+
if self.config.prefix != "":
|
|
416
|
+
prefix = self.config.prefix+"_"
|
|
417
|
+
else:
|
|
418
|
+
prefix = ""
|
|
419
|
+
|
|
420
|
+
litequeue = importlib.import_module("litequeue")
|
|
421
|
+
q = litequeue.LiteQueue(self.config.litequeue_database_name, queue_name=prefix+self.topic)
|
|
422
|
+
self.producer = q
|
|
423
|
+
self.consumer = q
|
|
424
|
+
except Exception as e:
|
|
425
|
+
Debug.error("Failed to import litequeue module", e)
|
|
426
|
+
sys.exit(1)
|
|
427
|
+
pass
|
|
428
|
+
|
|
429
|
+
def init_mongoqueue(self):
|
|
430
|
+
"""
|
|
431
|
+
Initializes mongo queue service
|
|
432
|
+
:return:
|
|
433
|
+
"""
|
|
434
|
+
try:
|
|
435
|
+
if self.config.prefix != "":
|
|
436
|
+
prefix = self.config.prefix+"_"
|
|
437
|
+
else:
|
|
438
|
+
prefix = ""
|
|
439
|
+
|
|
440
|
+
mongo_queue = importlib.import_module("mongo_queue")
|
|
441
|
+
pymongo = importlib.import_module("pymongo")
|
|
442
|
+
|
|
443
|
+
if self.config.mongo_queue_config is None:
|
|
444
|
+
self.config.mongo_queue_config = {"host": "localhost", "port": 27017, "timeout": 300, "max_attempts": 5}
|
|
445
|
+
|
|
446
|
+
if "username" in self.config.mongo_queue_config and "password" in self.config.mongo_queue_config:
|
|
447
|
+
client = pymongo.MongoClient(self.config.mongo_queue_config["host"],
|
|
448
|
+
self.config.mongo_queue_config["port"],
|
|
449
|
+
username=self.config.mongo_queue_config["username"],
|
|
450
|
+
password=self.config.mongo_queue_config["password"] ).queue
|
|
451
|
+
else:
|
|
452
|
+
client = pymongo.MongoClient(self.config.mongo_queue_config["host"],
|
|
453
|
+
self.config.mongo_queue_config["port"] ).queue
|
|
454
|
+
|
|
455
|
+
queue = mongo_queue.queue.Queue(client[prefix+self.topic],
|
|
456
|
+
consumer_id=self.topic, timeout=self.config.mongo_queue_config["timeout"],
|
|
457
|
+
max_attempts=self.config.mongo_queue_config["max_attempts"])
|
|
458
|
+
self.producer = queue
|
|
459
|
+
self.consumer = queue
|
|
460
|
+
except Exception as e:
|
|
461
|
+
Debug.error("Failed to import mongo_queue and mongo client, try pip install mongo-queue-service or poetry add mongo-queue-service", e)
|
|
462
|
+
pass
|
|
463
|
+
def init_rabbitmq(self):
|
|
464
|
+
try:
|
|
465
|
+
if self.config.prefix != "":
|
|
466
|
+
prefix = "/"+self.config.prefix
|
|
467
|
+
else:
|
|
468
|
+
prefix = "/"
|
|
469
|
+
|
|
470
|
+
if self.config.rabbitmq_config is None:
|
|
471
|
+
self.config.rabbitmq_config = {"host": "localhost", "port": 5672}
|
|
472
|
+
|
|
473
|
+
pika = importlib.import_module("pika")
|
|
474
|
+
|
|
475
|
+
try:
|
|
476
|
+
if "username" in self.config.rabbitmq_config:
|
|
477
|
+
credentials = pika.PlainCredentials(self.config.rabbitmq_config["username"],
|
|
478
|
+
self.config.rabbitmq_config["password"])
|
|
479
|
+
connection = pika.BlockingConnection(
|
|
480
|
+
pika.ConnectionParameters(host=self.config.rabbitmq_config["host"],
|
|
481
|
+
port=self.config.rabbitmq_config["port"],
|
|
482
|
+
virtual_host=prefix,
|
|
483
|
+
credentials=credentials
|
|
484
|
+
)
|
|
485
|
+
)
|
|
486
|
+
else:
|
|
487
|
+
connection = pika.BlockingConnection(
|
|
488
|
+
pika.ConnectionParameters(host=self.config.rabbitmq_config["host"],
|
|
489
|
+
port=self.config.rabbitmq_config["port"],
|
|
490
|
+
virtual_host=prefix
|
|
491
|
+
)
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
except Exception as e:
|
|
496
|
+
print(e)
|
|
497
|
+
channel = connection.channel()
|
|
498
|
+
channel.exchange_declare(exchange=self.topic, exchange_type="topic")
|
|
499
|
+
result = channel.queue_declare(self.topic, exclusive=False)
|
|
500
|
+
self.config.rabbitmq_queue = result.method.queue
|
|
501
|
+
channel.queue_bind(
|
|
502
|
+
exchange=self.topic, queue=self.config.rabbitmq_queue, routing_key='')
|
|
503
|
+
self.producer = channel
|
|
504
|
+
self.consumer = channel
|
|
505
|
+
|
|
506
|
+
except Exception as e:
|
|
507
|
+
Debug.error("Failed to import rabbitmq module, try pip install pika or poetry add pika", e)
|
|
508
|
+
pass
|
|
509
|
+
|
|
510
|
+
def init_kafka(self):
|
|
511
|
+
try:
|
|
512
|
+
if self.config.prefix != "":
|
|
513
|
+
prefix = self.config.prefix+"_"
|
|
514
|
+
else:
|
|
515
|
+
prefix = ""
|
|
516
|
+
|
|
517
|
+
if self.config.kafka_config is None:
|
|
518
|
+
self.config.kafka_config = {
|
|
519
|
+
# User-specific properties that you must set
|
|
520
|
+
'bootstrap.servers': 'localhost:9092',
|
|
521
|
+
'group.id': prefix+'default-queue',
|
|
522
|
+
'auto.offset.reset': 'earliest'
|
|
523
|
+
}
|
|
524
|
+
kafka = importlib.import_module("confluent_kafka")
|
|
525
|
+
|
|
526
|
+
self.consumer = kafka.Consumer(self.config.kafka_config)
|
|
527
|
+
self.consumer.subscribe([prefix+self.topic])
|
|
528
|
+
|
|
529
|
+
if 'auto.offset.reset' in self.config.kafka_config:
|
|
530
|
+
del self.config.kafka_config['auto.offset.reset']
|
|
531
|
+
if 'group.id' in self.config.kafka_config:
|
|
532
|
+
del self.config.kafka_config['group.id']
|
|
533
|
+
|
|
534
|
+
self.producer = kafka.Producer(self.config.kafka_config)
|
|
535
|
+
|
|
536
|
+
except Exception as e:
|
|
537
|
+
Debug.error("Failed to import kafka module, try pip install confluent-kafka or poetry add confluent-kafka", e)
|
|
538
|
+
pass
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
class Producer(object):
|
|
542
|
+
"""
|
|
543
|
+
Producer class to produce queues
|
|
544
|
+
"""
|
|
545
|
+
def __init__(self, queue, delivery_callback=None):
|
|
546
|
+
"""
|
|
547
|
+
Creates a producer to produce queues
|
|
548
|
+
:param queue:
|
|
549
|
+
:param delivery_callback:
|
|
550
|
+
"""
|
|
551
|
+
self.queue = queue
|
|
552
|
+
self.delivery_callback = delivery_callback
|
|
553
|
+
|
|
554
|
+
def produce(self, value, user_id=None, delivery_callback=None):
|
|
555
|
+
"""
|
|
556
|
+
Produces a message to queue
|
|
557
|
+
:param delivery_callback:
|
|
558
|
+
:param value:
|
|
559
|
+
:param user_id:
|
|
560
|
+
:return:
|
|
561
|
+
"""
|
|
562
|
+
if delivery_callback is None:
|
|
563
|
+
delivery_callback = self.delivery_callback
|
|
564
|
+
|
|
565
|
+
return self.queue.produce(value, user_id, delivery_callback)
|
|
566
|
+
|
|
567
|
+
class Consumer(object):
|
|
568
|
+
"""
|
|
569
|
+
Consumer class to consume queues
|
|
570
|
+
"""
|
|
571
|
+
def __init__(self, queues, consumer_callback=None, acknowledge=True):
|
|
572
|
+
"""
|
|
573
|
+
Creates a consumer to consume queues
|
|
574
|
+
:param queue:
|
|
575
|
+
:param topics:
|
|
576
|
+
:param consumer_callback:
|
|
577
|
+
"""
|
|
578
|
+
self.queues = queues
|
|
579
|
+
self.consumer_callback = consumer_callback
|
|
580
|
+
self.acknowledge = acknowledge
|
|
581
|
+
|
|
582
|
+
def run(self, sleep=1, iterations=None):
|
|
583
|
+
"""
|
|
584
|
+
Runs the consumer
|
|
585
|
+
:return:
|
|
586
|
+
"""
|
|
587
|
+
# Run until we crash
|
|
588
|
+
|
|
589
|
+
if not isinstance(self.queues, list):
|
|
590
|
+
self.queues = [self.queues]
|
|
591
|
+
|
|
592
|
+
try:
|
|
593
|
+
counter = 0
|
|
594
|
+
Debug("Consuming", self.queues)
|
|
595
|
+
while True:
|
|
596
|
+
for queue in self.queues:
|
|
597
|
+
try:
|
|
598
|
+
if queue.callback is not None:
|
|
599
|
+
callback = queue.callback
|
|
600
|
+
else:
|
|
601
|
+
callback = self.consumer_callback
|
|
602
|
+
queue.consume(self.acknowledge, callback)
|
|
603
|
+
except Exception as e:
|
|
604
|
+
Debug("Queue Consumer Exception", str(e))
|
|
605
|
+
counter += 1
|
|
606
|
+
if iterations is not None and counter >= iterations:
|
|
607
|
+
break
|
|
608
|
+
time.sleep(sleep)
|
|
609
|
+
except KeyboardInterrupt:
|
|
610
|
+
pass
|
|
611
|
+
finally:
|
|
612
|
+
Debug.info("Done running consumer")
|
|
613
|
+
pass
|
|
614
|
+
|
|
615
|
+
|
tina4_python/Request.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Tina4 - This is not a 4ramework.
|
|
3
|
+
# Copy-right 2007 - current Tina4
|
|
4
|
+
# License: MIT https://opensource.org/licenses/MIT
|
|
5
|
+
#
|
|
6
|
+
# flake8: noqa: E501
|
|
7
|
+
request = None
|
|
8
|
+
body = None
|
|
9
|
+
params = {}
|
|
10
|
+
headers = {}
|
|
11
|
+
cookies = {}
|
|
12
|
+
url = None
|
|
13
|
+
session = None
|
|
14
|
+
files = {}
|
|
15
|
+
raw_request = None
|
|
16
|
+
raw_data = None
|
|
17
|
+
raw_content = None
|
|
18
|
+
transport = None
|
|
19
|
+
asgi_response = None
|