django-restit 4.2.167__py3-none-any.whl → 4.2.173__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: django-restit
3
- Version: 4.2.167
3
+ Version: 4.2.173
4
4
  Summary: A Rest Framework for DJANGO
5
5
  License: MIT
6
6
  Author: Ian Starnes
@@ -13,6 +13,7 @@ Classifier: Programming Language :: Python :: 3.9
13
13
  Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
16
17
  Requires-Dist: boto3 (>=1.26.160,<2.0.0)
17
18
  Requires-Dist: django
18
19
  Requires-Dist: django-redis-cache (>=3.0.1,<4.0.0)
@@ -356,7 +356,7 @@ metrics/migrations/0003_metrics_expires.py,sha256=_g4oRv4NHW-4iCQx2s1SiF38LLyFf8
356
356
  metrics/migrations/0004_eodmetrics.py,sha256=Ky6ZVMZqa0F_SUp_QFWY7ZKBgVhy9CS4wZcsEhrkSgc,3271
357
357
  metrics/migrations/0005_alter_metrics_v1_alter_metrics_v10_alter_metrics_v11_and_more.py,sha256=pmwJfpPJ1RUX_CqM66l6vvV-nrAUPo_GIan0Pc9mQHs,2358
358
358
  metrics/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
359
- metrics/models.py,sha256=-bIEvhamfZZKweMEswBcBoZDOQhPqJHbCKyUuH6WjFc,14052
359
+ metrics/models.py,sha256=O29Cud4VAwIAWnmQymFti0-ibOEoGmTWGxWsbtzcZHQ,14162
360
360
  metrics/periodic.py,sha256=IayBLLat40D8FB-A3bYBW9lxm9-IzcugQunojThQ_OU,661
361
361
  metrics/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
362
362
  metrics/providers/aws.py,sha256=dIBGFE1Fvszy6rmVrn_Fm1zUDv345q4dBsg9Iit-XCc,8358
@@ -379,7 +379,7 @@ pushit/utils.py,sha256=IeTCGa-164nmB1jIsK1lu1O1QzUhS3BKfuXHGjCW-ck,2121
379
379
  rest/.gitignore,sha256=TbEvWRMnAiajCTOdhiNrd9eeCAaIjRp9PRjE_VkMM5g,118
380
380
  rest/README.md,sha256=V3ETc-cJu8PZIbKr9xSe_pA4JEUpC8Dhw4bQeVCDJPw,5460
381
381
  rest/RemoteEvents.py,sha256=nL46U7AuxIrlw2JunphR1tsXyqi-ep_gD9CYGpYbNgE,72
382
- rest/__init__.py,sha256=eBXKeTBaSN_87SpMKDrp3hqPYpVdDv_zx8hKsvRrrbI,122
382
+ rest/__init__.py,sha256=X-ZjYMsIpHcsnYPqEyCUT_cVtex5VAn-DlgZF8JJFg0,122
383
383
  rest/arc4.py,sha256=y644IbF1ec--e4cUJ3KEYsewTCITK0gmlwa5mJruFC0,1967
384
384
  rest/cache.py,sha256=1Qg0rkaCJCaVP0-l5hZg2CIblTdeBSlj_0fP6vlKUpU,83
385
385
  rest/crypto/__init__.py,sha256=Tl0U11rgj1eBYqd6OXJ2_XSdNLumW_JkBZnaJqI6Ldw,72
@@ -461,7 +461,7 @@ taskqueue/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
461
461
  taskqueue/admin.py,sha256=E6zXoToS_ea3MdoGjZzF1JiepWFtDSoZUQdan8H-pXI,208
462
462
  taskqueue/migrations/0001_initial.py,sha256=JwYib8CK5ftSXlfxKZUcKEEVsXktNB5q3h-2tu9inGk,4738
463
463
  taskqueue/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
464
- taskqueue/models.py,sha256=aRy5TFc2zAu6xcb3jcMf-bfma-dmrh55hQoktCLT0Us,23894
464
+ taskqueue/models.py,sha256=XqgQDKaxKAfqSkom0VF2HeUwFXpvM46ikZdvKXUKAz8,23843
465
465
  taskqueue/periodic.py,sha256=hpXnunJL_cuVQLAKpjTbABbsQ4fvdsV9_gyyK-_53Sk,3844
466
466
  taskqueue/rpc.py,sha256=Lf5VUoqCRkfWUAIvx_s508mjAtDPwpiWyxg0ryqWbQA,5793
467
467
  taskqueue/tq.py,sha256=PzSoDrawYcqZylruEgsK95gcJ4J_VhdM6rxg9V6_X8E,942
@@ -471,7 +471,7 @@ taskqueue/transports/http.py,sha256=AzliUnw_LuyO2zZZOoUAJGFcTV-Gxt1iE3hCVnIiyGQ,
471
471
  taskqueue/transports/s3.py,sha256=fMosL893u1iQdo6Y1djwb7KEoNo6TTsDPJl13OJdJP8,1913
472
472
  taskqueue/transports/sftp.py,sha256=jT1_krjTHA7DCAukD85aGYRCg9m0cEH9EWzOC-wJGdk,1891
473
473
  taskqueue/transports/sms.py,sha256=H1-LIGEMfbUNqJD9amRcsvKUSwtz9yBj1QNfB7EHjHE,142
474
- taskqueue/worker.py,sha256=U_WGw3pJx9-I8loCQyfj3kyWScXOVb45g-6U9uyCH-c,15700
474
+ taskqueue/worker.py,sha256=91n_GuwB-keozkyrgYcDpFUybV6EC2Yirnv8owPUwrg,16250
475
475
  telephony/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
476
476
  telephony/admin.py,sha256=iOdsBfFFbBisdqKSZ36bIrh_z5sU0Wx_PkaFi8wd1iA,243
477
477
  telephony/decorators.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -515,7 +515,7 @@ ws4redis/servers/uwsgi.py,sha256=VyhoCI1DnVFqBiJYHoxqn5Idlf6uJPHvfBKgkjs34mo,172
515
515
  ws4redis/settings.py,sha256=KKq00EwoGnz1yLwCZr5Dfoq2izivmAdsNEEM4EhZwN4,1610
516
516
  ws4redis/utf8validator.py,sha256=S0OlfjeGRP75aO6CzZsF4oTjRQAgR17OWE9rgZdMBZA,5122
517
517
  ws4redis/websocket.py,sha256=R0TUyPsoVRD7Y_oU7w2I6NL4fPwiz5Vl94-fUkZgLHA,14848
518
- django_restit-4.2.167.dist-info/LICENSE.md,sha256=VHN4hhEeVOoFjtG-5fVv4jesA4SWi0Z-KgOzzN6a1ps,1068
519
- django_restit-4.2.167.dist-info/METADATA,sha256=6AEOSN6ApDMK6jAJsPqm3T4I3EJLbO894J6lhA5p_4E,7663
520
- django_restit-4.2.167.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
521
- django_restit-4.2.167.dist-info/RECORD,,
518
+ django_restit-4.2.173.dist-info/LICENSE.md,sha256=VHN4hhEeVOoFjtG-5fVv4jesA4SWi0Z-KgOzzN6a1ps,1068
519
+ django_restit-4.2.173.dist-info/METADATA,sha256=i08Bup9bGciei5ZmKtc-1LMW236KZYtfSWUByHv6tfo,7714
520
+ django_restit-4.2.173.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
521
+ django_restit-4.2.173.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.0
2
+ Generator: poetry-core 2.0.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
metrics/models.py CHANGED
@@ -40,6 +40,8 @@ def metric(
40
40
  expires = datetime.now() + timedelta(days=METRICS_EXPIRE_DAILY)
41
41
  elif granularity == "weekly":
42
42
  expires = datetime.now() + timedelta(days=METRICS_EXPIRE_WEEKLY)
43
+ elif granularity in ["minutes", "seconds"]:
44
+ expires = datetime.now() + timedelta(days=7)
43
45
  m, created = Metrics.objects.get_or_create(
44
46
  uuid=key,
45
47
  defaults=dict(
@@ -88,6 +90,7 @@ def normalize_date(date=None, timezone=None, group=None):
88
90
  date = date_util.convertToLocalTime(settings.METRICS_TIMEZONE, date)
89
91
  return date
90
92
 
93
+
91
94
  def get_qset(slug, granularity, start=None, end=None,
92
95
  group=-1, starts_with=False, ends_with=None):
93
96
  if start is None:
rest/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  from .uberdict import UberDict # noqa: F401
2
2
  from .settings_helper import settings # noqa: F401
3
3
 
4
- __version__ = "4.2.167"
4
+ __version__ = "4.2.173"
taskqueue/models.py CHANGED
@@ -166,8 +166,8 @@ class Task(models.Model, RestModel):
166
166
  self.retry_now()
167
167
  elif value == "cancel":
168
168
  request = self.getActiveRequest()
169
- self.reason = "cancel request by {}".format(request.member.username)
170
- self.cancel(request.DATA.get("reason", "canceled by {}".format(request.member.username)))
169
+ self.reason = f"cancel request by {request.member.username}"
170
+ self.cancel(request.DATA.get("reason", f"canceled by {request.member.username}"))
171
171
 
172
172
  # def auditlog(self, action, message, request=None, path=None, component="taskqueue.Task"):
173
173
  # # message, level=0, request=None, component=None, pkey=None, action=None, group=None, path=None, method=None
@@ -183,7 +183,7 @@ class Task(models.Model, RestModel):
183
183
  level = 21
184
184
  elif kind == "error":
185
185
  level = 17
186
- PLOG.log(message=text, action=f"taskqueue_{kind}",
186
+ PLOG.log(message=text, action=f"taskqueue_{kind}",
187
187
  level=level,
188
188
  method=self.fname, path=self.model,
189
189
  tid=self.data.log_tid,
@@ -290,7 +290,7 @@ class Task(models.Model, RestModel):
290
290
  try:
291
291
  import incident
292
292
  incident.event_now(
293
- category, description=subject, details=msg,
293
+ category, description=subject, details=msg,
294
294
  level=3, **metadata)
295
295
  except Exception as err:
296
296
  self.log(str(err), kind="error")
@@ -395,7 +395,7 @@ class Task(models.Model, RestModel):
395
395
  task.save()
396
396
  task.publish()
397
397
  return task
398
-
398
+
399
399
  @classmethod
400
400
  def S3Request(cls, bucket, data, folder, aws, secret, filename, when,
401
401
  log_component=None, log_pk=None, log_tid=None):
@@ -458,7 +458,6 @@ class TaskWorkerClient:
458
458
  uid = crypto.randomString(16)
459
459
  self.recv_channel = f"tq:{uid}:{settings.HOSTNAME}"
460
460
  self.send_channel = f"tq:host:{hostname}"
461
-
462
461
  self.client = None
463
462
  self.pubsub = None
464
463
 
@@ -480,7 +479,7 @@ class TaskWorkerClient:
480
479
  data["hostname"] = settings.HOSTNAME
481
480
  data["response_channel"] = self.recv_channel
482
481
  redis.publish(
483
- self.send_channel,
482
+ self.send_channel,
484
483
  data, self.client)
485
484
 
486
485
  def recv(self, timeout=5.0):
@@ -492,7 +491,7 @@ class TaskWorkerClient:
492
491
  if msg.data:
493
492
  msg.data = nobjict.fromJSON(msg.data)
494
493
  return msg.data
495
-
494
+
496
495
  def ping(self):
497
496
  start = time.perf_counter()
498
497
  self.send(dict(action="ping"))
@@ -644,6 +643,3 @@ class TaskHook(models.Model, RestModel, MetaDataModel):
644
643
 
645
644
  class TaskHookMetaData(MetaDataBase):
646
645
  parent = models.ForeignKey(TaskHook, related_name="properties", on_delete=models.CASCADE)
647
-
648
-
649
-
taskqueue/worker.py CHANGED
@@ -62,7 +62,7 @@ class WorkManager(object):
62
62
  task.failed("stale")
63
63
  return
64
64
  if task.id in self._scheduled_tasks:
65
- self.logger.error("task({}) is alrleady scheduled".format(task.id))
65
+ self.logger.error("task({}) is already scheduled".format(task.id))
66
66
  return
67
67
  task.manager = self
68
68
  with self.lock:
@@ -84,10 +84,10 @@ class WorkManager(object):
84
84
  if event.channel == "tq_restart":
85
85
  self.restart()
86
86
  return
87
-
87
+
88
88
  if event.data:
89
89
  event.data = nobjict.fromJSON(event.data)
90
-
90
+
91
91
  if event.channel == self.host_channel:
92
92
  self.on_host_event(event)
93
93
  return
@@ -198,6 +198,7 @@ class WorkManager(object):
198
198
  task.failed("max attempts")
199
199
 
200
200
  def _on_hookrequest(self, task):
201
+ resp = None
201
202
  if task.model == "tq_email_request":
202
203
  resp = email.SEND(task)
203
204
  elif task.model == "tq_sms_request":
@@ -223,7 +224,7 @@ class WorkManager(object):
223
224
  redis.publish(
224
225
  event.data.response_channel,
225
226
  dict(action="restarting", hostname=settings.HOSTNAME))
226
- self.restart()
227
+ self.restart()
227
228
  elif action == "get_stats":
228
229
  data = nobjict(action="stats", hostname=settings.HOSTNAME)
229
230
  data.uptime = time.time() - self.started_at
@@ -240,83 +241,107 @@ class WorkManager(object):
240
241
  task.worker_running = True
241
242
  self._running_count += 1
242
243
  self._pending_count -= 1
244
+ # remove the task from scheduled
245
+ del self._scheduled_tasks[task.id]
243
246
  self.updateCounts()
244
247
 
245
248
  def on_task_ended(self, task):
246
249
  with self.lock:
247
250
  task.worker_running = False
248
- del self._scheduled_tasks[task.id]
249
251
  self._running_count -= 1
250
252
  self.updateCounts()
251
253
 
252
254
  def on_run_task(self, task):
255
+ """ Handles execution of a task with structured error handling and refactored logic. """
256
+
253
257
  self.logger.info("running task({})".format(task.id))
254
258
 
255
- # check if its canceled
259
+ # Start task and handle cancel/stale conditions early
260
+ if not self._initialize_task(task):
261
+ return
262
+
263
+ # Get the appropriate handler for the task
264
+ handler = self._get_task_handler(task)
265
+ if handler is None:
266
+ task.failed("failed to find handler")
267
+ return self._end_task(task)
268
+
269
+ # Execute task
270
+ self._execute_task(task, handler)
271
+
272
+ def _initialize_task(self, task):
273
+ """ Initializes task and checks if it should run. Returns False if the task should stop. """
256
274
  try:
257
275
  self.on_task_started(task)
258
276
  task.refresh_from_db()
259
277
  task._thread_id = threading.current_thread().ident
260
278
  self.logger.debug("running on thread:{}".format(task._thread_id))
279
+
261
280
  if task.state not in [0, 1, 2, 10] or task.cancel_requested:
262
281
  self.logger.info("task({}) was canceled?".format(task.id))
263
- self.on_task_ended(task)
264
- return
282
+ return self._end_task(task)
265
283
 
266
284
  if task.is_stale:
267
285
  self.logger.warning("task({}) is now stale".format(task.id))
268
286
  task.failed("stale")
269
- self.on_task_ended(task)
270
- return
287
+ return self._end_task(task)
288
+
289
+ return True
271
290
  except Exception as err:
272
291
  self.logger.exception(err)
273
- return
292
+ return False
274
293
 
275
- handler = None
276
- if task.model == "tq_web_request":
277
- handler = self._on_webrequest
278
- elif task.model in ["tq_sftp_request", "tq_email_request", "tq_sms_request", "tq_s3_request"]:
279
- handler = self._on_hookrequest
280
- else:
281
- try:
282
- handler = task.getHandler()
283
- except Exception as err:
284
- self.logger.exception(err)
285
- task.log_exception(err)
286
- task.failed(str(err))
287
- self.on_task_ended(task)
288
- return
294
+ def _get_task_handler(self, task):
295
+ """ Determines the appropriate handler for the task. """
296
+ task_handlers = {
297
+ "tq_web_request": self._on_webrequest,
298
+ "tq_sftp_request": self._on_hookrequest,
299
+ "tq_email_request": self._on_hookrequest,
300
+ "tq_sms_request": self._on_hookrequest,
301
+ "tq_s3_request": self._on_hookrequest,
302
+ }
289
303
 
290
- if handler is None:
291
- self.logger.error("failed to find handler: task({})".format(task.id))
292
- task.failed("failed to find handler")
293
- self.on_task_ended(task)
294
- return
304
+ handler = task_handlers.get(task.model)
305
+ if handler:
306
+ return handler
307
+
308
+ try:
309
+ return task.getHandler()
310
+ except Exception as err:
311
+ self.logger.exception(err)
312
+ task.log_exception(err)
313
+ task.failed(str(err))
314
+ self._end_task(task)
315
+ return None
295
316
 
296
- # self.logger.debug("task.started()")
317
+ def _execute_task(self, task, handler):
318
+ """ Runs the task handler and manages exceptions. """
297
319
  task.started()
298
320
  try:
299
- # self.logger.debug("task({}) calling handler".format(task.id))
300
321
  handler(task)
301
322
  if task.state == TASK_STATE_STARTED:
302
323
  task.completed()
303
- # self.logger.debug("task({}) handler finished".format(task.id))
304
324
  except Exception as err:
305
- self.logger.exception(err, "task({}) had exception".format(task.id))
306
- task.log_exception(err)
307
- if "connection already closed" in str(err).lower():
308
- # this is a nasty little bug in django when forking django db connections
309
- # we will schedule the task to retry later
310
- task.retry_later()
311
- # let us try and close db connections?
312
- hack_closeDjangoDB()
313
- # or should we restart the task queue?
314
- else:
315
- task.failed(str(err))
325
+ self._handle_task_exception(task, err)
316
326
  except SystemExit:
317
327
  self.logger.error("task({}) was killed".format(task.id))
318
328
  finally:
319
- self.on_task_ended(task)
329
+ self._end_task(task)
330
+
331
+ def _handle_task_exception(self, task, err):
332
+ """ Handles exceptions during task execution. """
333
+ self.logger.exception("task({}) had exception: {}".format(task.id, err))
334
+ task.log_exception(err)
335
+
336
+ if "connection already closed" in str(err).lower():
337
+ task.retry_later()
338
+ hack_closeDjangoDB()
339
+ else:
340
+ task.failed(str(err))
341
+
342
+ def _end_task(self, task):
343
+ """ Ensures proper cleanup and logging at the end of the task. """
344
+ self.on_task_ended(task)
320
345
  self.logger.info("task({}) finished with state {}".format(task.id, task.state))
321
346
 
322
347
  def run_forever(self):
@@ -420,5 +445,3 @@ def hack_closeDjangoDB():
420
445
  from django.db import connections
421
446
  for conn in connections.all():
422
447
  conn.close_if_unusable_or_obsolete()
423
-
424
-