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.
Files changed (47) hide show
  1. tina4_python/Auth.py +222 -0
  2. tina4_python/Constant.py +43 -0
  3. tina4_python/Database.py +591 -0
  4. tina4_python/DatabaseResult.py +107 -0
  5. tina4_python/DatabaseTypes.py +15 -0
  6. tina4_python/Debug.py +126 -0
  7. tina4_python/Env.py +37 -0
  8. tina4_python/Localization.py +42 -0
  9. tina4_python/Messages.py +30 -0
  10. tina4_python/MiddleWare.py +90 -0
  11. tina4_python/Migration.py +107 -0
  12. tina4_python/ORM.py +639 -0
  13. tina4_python/Queue.py +615 -0
  14. tina4_python/Request.py +19 -0
  15. tina4_python/Response.py +121 -0
  16. tina4_python/Router.py +423 -0
  17. tina4_python/Session.py +342 -0
  18. tina4_python/ShellColors.py +20 -0
  19. tina4_python/Swagger.py +228 -0
  20. tina4_python/Template.py +107 -0
  21. tina4_python/Webserver.py +429 -0
  22. tina4_python/Websocket.py +49 -0
  23. tina4_python/__init__.py +392 -0
  24. tina4_python/messages.pot +83 -0
  25. tina4_python/public/css/readme.md +0 -0
  26. tina4_python/public/favicon.ico +0 -0
  27. tina4_python/public/images/403.png +0 -0
  28. tina4_python/public/images/404.png +0 -0
  29. tina4_python/public/images/500.png +0 -0
  30. tina4_python/public/images/logo.png +0 -0
  31. tina4_python/public/images/readme.md +0 -0
  32. tina4_python/public/js/readme.md +0 -0
  33. tina4_python/public/js/reconnecting-websocket.js +365 -0
  34. tina4_python/public/js/tina4helper.js +397 -0
  35. tina4_python/public/swagger/index.html +90 -0
  36. tina4_python/public/swagger/oauth2-redirect.html +63 -0
  37. tina4_python/templates/errors/403.twig +10 -0
  38. tina4_python/templates/errors/404.twig +10 -0
  39. tina4_python/templates/errors/500.twig +11 -0
  40. tina4_python/templates/readme.md +1 -0
  41. tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
  42. tina4_python/translations/en/LC_MESSAGES/messages.po +80 -0
  43. tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
  44. tina4_python/translations/fr/LC_MESSAGES/messages.po +84 -0
  45. tina4_python-0.2.122.dist-info/METADATA +465 -0
  46. tina4_python-0.2.122.dist-info/RECORD +47 -0
  47. 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
+
@@ -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