dycw-utilities 0.129.1__py3-none-any.whl → 0.129.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.129.1
3
+ Version: 0.129.3
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -1,6 +1,6 @@
1
- utilities/__init__.py,sha256=SD3J-kHQFgs1qNwFwV6LhMTAXj4jsrYuL05YluwVtcE,60
1
+ utilities/__init__.py,sha256=xvWWvcsivl7tOOnGKv9OKQ27M1f_gdDFBYwkYVERk-4,60
2
2
  utilities/altair.py,sha256=Gpja-flOo-Db0PIPJLJsgzAlXWoKUjPU1qY-DQ829ek,9156
3
- utilities/asyncio.py,sha256=OIQ4JddpQw8tSubzwDR0WyqQ-uE-L5DdbwuTqRQK5MQ,38202
3
+ utilities/asyncio.py,sha256=3n5EIcSq2xtEF1i4oR0oY2JmBq3NyugeHKFK39Mt22s,37987
4
4
  utilities/atomicwrites.py,sha256=geFjn9Pwn-tTrtoGjDDxWli9NqbYfy3gGL6ZBctiqSo,5393
5
5
  utilities/atools.py,sha256=IYMuFSFGSKyuQmqD6v5IUtDlz8PPw0Sr87Cub_gRU3M,1168
6
6
  utilities/cachetools.py,sha256=C1zqOg7BYz0IfQFK8e3qaDDgEZxDpo47F15RTfJM37Q,2910
@@ -89,7 +89,7 @@ utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
89
89
  utilities/whenever.py,sha256=jS31ZAY5OMxFxLja_Yo5Fidi87Pd-GoVZ7Vi_teqVDA,16743
90
90
  utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
91
91
  utilities/zoneinfo.py,sha256=-5j7IQ9nb7gR43rdgA7ms05im-XuqhAk9EJnQBXxCoQ,1874
92
- dycw_utilities-0.129.1.dist-info/METADATA,sha256=5y1_o16EDF5UAXj97fupC2XxHo_SqHwLxYDo22_4xe0,12803
93
- dycw_utilities-0.129.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
94
- dycw_utilities-0.129.1.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
95
- dycw_utilities-0.129.1.dist-info/RECORD,,
92
+ dycw_utilities-0.129.3.dist-info/METADATA,sha256=tSQGMjW1iN-3qoQebYNCtjnI6yzQARSXvw0ThdEKy2A,12803
93
+ dycw_utilities-0.129.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
94
+ dycw_utilities-0.129.3.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
95
+ dycw_utilities-0.129.3.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.129.1"
3
+ __version__ = "0.129.3"
utilities/asyncio.py CHANGED
@@ -353,6 +353,7 @@ class Looper(Generic[_T]):
353
353
  _is_entered: Event = field(default_factory=Event, init=False, repr=False)
354
354
  _is_initialized: Event = field(default_factory=Event, init=False, repr=False)
355
355
  _is_initializing: Event = field(default_factory=Event, init=False, repr=False)
356
+ _is_pending_back_off: Event = field(default_factory=Event, init=False, repr=False)
356
357
  _is_pending_restart: Event = field(default_factory=Event, init=False, repr=False)
357
358
  _is_pending_stop: Event = field(default_factory=Event, init=False, repr=False)
358
359
  _is_pending_stop_when_empty: Event = field(
@@ -439,6 +440,11 @@ class Looper(Generic[_T]):
439
440
  def __len__(self) -> int:
440
441
  return self._queue.qsize()
441
442
 
443
+ async def _apply_back_off(self) -> None:
444
+ """Apply a back off period."""
445
+ await sleep(self._backoff)
446
+ self._is_pending_back_off.clear()
447
+
442
448
  async def core(self) -> None:
443
449
  """Core part of running the looper."""
444
450
 
@@ -458,7 +464,9 @@ class Looper(Generic[_T]):
458
464
  """Remove and return an item from the end of the queue without blocking."""
459
465
  return self._queue.get_right_nowait()
460
466
 
461
- async def initialize(self, *, sleep_if_failure: bool) -> Exception | None:
467
+ async def initialize(
468
+ self, *, skip_sleep_if_failure: bool = False
469
+ ) -> Exception | None:
462
470
  """Initialize the looper."""
463
471
  match self._is_initializing.is_set():
464
472
  case True:
@@ -476,21 +484,21 @@ class Looper(Generic[_T]):
476
484
  async with self._lock:
477
485
  self._initialization_failures += 1
478
486
  ret = error
479
- match sleep_if_failure:
487
+ match skip_sleep_if_failure:
480
488
  case True:
481
489
  _ = self._logger.warning(
482
- "%s: encountered %s whilst initializing; sleeping for %s...",
490
+ "%s: encountered %s whilst initializing",
483
491
  self,
484
492
  repr_error(error),
485
- self.backoff,
486
493
  )
487
- await sleep(self._backoff)
488
494
  case False:
489
495
  _ = self._logger.warning(
490
- "%s: encountered %s whilst initializing",
496
+ "%s: encountered %s whilst initializing; sleeping for %s...",
491
497
  self,
492
498
  repr_error(error),
499
+ self.backoff,
493
500
  )
501
+ await self._apply_back_off()
494
502
  case _ as never:
495
503
  assert_never(never)
496
504
  else:
@@ -547,6 +555,21 @@ class Looper(Generic[_T]):
547
555
  **kwargs,
548
556
  )
549
557
 
558
+ def request_back_off(self) -> None:
559
+ """Request the looper to back off."""
560
+ match self._is_pending_back_off.is_set():
561
+ case True:
562
+ _ = self._debug and self._logger.debug(
563
+ "%s: already requested back off", self
564
+ )
565
+ case False:
566
+ _ = self._debug and self._logger.debug(
567
+ "%s: requesting back off...", self
568
+ )
569
+ self._is_pending_back_off.set()
570
+ case _ as never:
571
+ assert_never(never)
572
+
550
573
  def request_restart(self) -> None:
551
574
  """Request the looper to restart."""
552
575
  match self._is_pending_restart.is_set():
@@ -561,6 +584,7 @@ class Looper(Generic[_T]):
561
584
  self._is_pending_restart.set()
562
585
  case _ as never:
563
586
  assert_never(never)
587
+ self.request_back_off()
564
588
 
565
589
  def request_stop(self) -> None:
566
590
  """Request the looper to stop."""
@@ -590,20 +614,20 @@ class Looper(Generic[_T]):
590
614
  case _ as never:
591
615
  assert_never(never)
592
616
 
593
- async def restart(self, *, sleep_if_failure: bool) -> None:
617
+ async def restart(self) -> None:
594
618
  """Restart the looper."""
595
619
  _ = self._debug and self._logger.debug("%s: restarting...", self)
596
620
  self._is_pending_restart.clear()
597
621
  async with self._lock:
598
622
  self._restart_attempts += 1
599
- tear_down = await self.tear_down(sleep_if_failure=False)
600
- initialization = await self.initialize(sleep_if_failure=False)
601
- match tear_down, initialization, sleep_if_failure:
602
- case None, None, bool():
623
+ tear_down = await self.tear_down(skip_sleep_if_failure=True)
624
+ initialization = await self.initialize(skip_sleep_if_failure=True)
625
+ match tear_down, initialization:
626
+ case None, None:
603
627
  _ = self._debug and self._logger.debug("%s: finished restarting", self)
604
628
  async with self._lock:
605
629
  self._restart_successes += 1
606
- case Exception(), None, True:
630
+ case Exception(), None:
607
631
  async with self._lock:
608
632
  self._restart_failures += 1
609
633
  _ = self._logger.warning(
@@ -612,16 +636,8 @@ class Looper(Generic[_T]):
612
636
  repr_error(tear_down),
613
637
  self.backoff,
614
638
  )
615
- await sleep(self._backoff)
616
- case Exception(), None, False:
617
- async with self._lock:
618
- self._restart_failures += 1
619
- _ = self._logger.warning(
620
- "%s: encountered %s whilst restarting (tear down)",
621
- self,
622
- repr_error(tear_down),
623
- )
624
- case None, Exception(), True:
639
+ await self._apply_back_off()
640
+ case None, Exception():
625
641
  async with self._lock:
626
642
  self._restart_failures += 1
627
643
  _ = self._logger.warning(
@@ -630,16 +646,8 @@ class Looper(Generic[_T]):
630
646
  repr_error(initialization),
631
647
  self.backoff,
632
648
  )
633
- await sleep(self._backoff)
634
- case None, Exception(), False:
635
- async with self._lock:
636
- self._restart_failures += 1
637
- _ = self._logger.warning(
638
- "%s: encountered %s whilst restarting (initialize)",
639
- self,
640
- repr_error(initialization),
641
- )
642
- case Exception(), Exception(), True:
649
+ await self._apply_back_off()
650
+ case Exception(), Exception():
643
651
  async with self._lock:
644
652
  self._restart_failures += 1
645
653
  _ = self._logger.warning(
@@ -649,16 +657,7 @@ class Looper(Generic[_T]):
649
657
  repr_error(initialization),
650
658
  self.backoff,
651
659
  )
652
- await sleep(self._backoff)
653
- case Exception(), Exception(), False:
654
- async with self._lock:
655
- self._restart_failures += 1
656
- _ = self._logger.warning(
657
- "%s: encountered %s (tear down) and then %s (initialization) whilst restarting",
658
- self,
659
- repr_error(tear_down),
660
- repr_error(initialization),
661
- )
660
+ await self._apply_back_off()
662
661
  case _ as never:
663
662
  assert_never(never)
664
663
 
@@ -674,10 +673,12 @@ class Looper(Generic[_T]):
674
673
  self._is_pending_stop_when_empty.is_set() and self.empty()
675
674
  ):
676
675
  await self.stop()
676
+ elif self._is_pending_back_off.is_set():
677
+ await self._apply_back_off()
677
678
  elif self._is_pending_restart.is_set():
678
- await self.restart(sleep_if_failure=True)
679
+ await self.restart()
679
680
  elif not self._is_initialized.is_set():
680
- _ = await self.initialize(sleep_if_failure=True)
681
+ _ = await self.initialize()
681
682
  else:
682
683
  _ = self._debug and self._logger.debug(
683
684
  "%s: running core...", self
@@ -695,7 +696,6 @@ class Looper(Generic[_T]):
695
696
  async with self._lock:
696
697
  self._core_failures += 1
697
698
  self.request_restart()
698
- await sleep(self._backoff)
699
699
  else:
700
700
  async with self._lock:
701
701
  self._core_successes += 1
@@ -749,7 +749,9 @@ class Looper(Generic[_T]):
749
749
  case _ as never:
750
750
  assert_never(never)
751
751
 
752
- async def tear_down(self, *, sleep_if_failure: bool) -> Exception | None:
752
+ async def tear_down(
753
+ self, *, skip_sleep_if_failure: bool = False
754
+ ) -> Exception | None:
753
755
  """Tear down the looper."""
754
756
  match self._is_tearing_down.is_set():
755
757
  case True:
@@ -766,21 +768,21 @@ class Looper(Generic[_T]):
766
768
  async with self._lock:
767
769
  self._tear_down_failures += 1
768
770
  ret = error
769
- match sleep_if_failure:
771
+ match skip_sleep_if_failure:
770
772
  case True:
771
773
  _ = self._logger.warning(
772
- "%s: encountered %s whilst tearing down; sleeping for %s...",
774
+ "%s: encountered %s whilst tearing down",
773
775
  self,
774
776
  repr_error(error),
775
- self.backoff,
776
777
  )
777
- await sleep(self._backoff)
778
778
  case False:
779
779
  _ = self._logger.warning(
780
- "%s: encountered %s whilst tearing down",
780
+ "%s: encountered %s whilst tearing down; sleeping for %s...",
781
781
  self,
782
782
  repr_error(error),
783
+ self.backoff,
783
784
  )
785
+ await self._apply_back_off()
784
786
  case _ as never:
785
787
  assert_never(never)
786
788
  else: