streamlit-octostar-utils 0.5.0.dev19__tar.gz → 0.5.1.dev1__tar.gz

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 (45) hide show
  1. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/PKG-INFO +1 -1
  2. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/pyproject.toml +1 -1
  3. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/api_crafter/celery.py +68 -32
  4. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/LICENSE +0 -0
  5. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/README.md +0 -0
  6. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/__init__.py +0 -0
  7. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/api_crafter/__init__.py +0 -0
  8. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/api_crafter/contents.py +0 -0
  9. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/api_crafter/fastapi.py +0 -0
  10. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/api_crafter/nifi.py +0 -0
  11. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/api_crafter/parallelism.py +0 -0
  12. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/api_crafter/parser/__init__.py +0 -0
  13. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/api_crafter/parser/combine_fields.py +0 -0
  14. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/api_crafter/parser/entities_parser.py +0 -0
  15. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/api_crafter/parser/generics.py +0 -0
  16. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/api_crafter/parser/info.py +0 -0
  17. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/api_crafter/parser/linkchart_functions.py +0 -0
  18. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/api_crafter/parser/matches.py +0 -0
  19. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/api_crafter/parser/parameters.py +0 -0
  20. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/api_crafter/parser/rules.py +0 -0
  21. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/api_crafter/parser/signals.py +0 -0
  22. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/core/__init__.py +0 -0
  23. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/core/dict.py +0 -0
  24. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/core/filetypes.py +0 -0
  25. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/core/threading/__init__.py +0 -0
  26. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/core/threading/key_queue.py +0 -0
  27. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/core/timestamp.py +0 -0
  28. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/nlp/__init__.py +0 -0
  29. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/nlp/custom_recognizers.py +0 -0
  30. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/nlp/language.py +0 -0
  31. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/nlp/ner.py +0 -0
  32. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/octostar/__init__.py +0 -0
  33. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/octostar/client.py +0 -0
  34. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/octostar/context.py +0 -0
  35. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/octostar/permissions.py +0 -0
  36. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/ontology/__init__.py +0 -0
  37. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/ontology/inheritance.py +0 -0
  38. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/ontology/relationships.py +0 -0
  39. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/ontology/validation.py +0 -0
  40. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/style/__init__.py +0 -0
  41. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/style/common.py +0 -0
  42. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/threading/__init__.py +0 -0
  43. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/threading/async_task_manager.py +0 -0
  44. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/threading/session_callback_manager.py +0 -0
  45. {streamlit_octostar_utils-0.5.0.dev19 → streamlit_octostar_utils-0.5.1.dev1}/streamlit_octostar_utils/threading/session_state_hot_swapper.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: streamlit-octostar-utils
3
- Version: 0.5.0.dev19
3
+ Version: 0.5.1.dev1
4
4
  Summary:
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -5,7 +5,7 @@ include = '\.pyi?$'
5
5
 
6
6
  [tool.poetry]
7
7
  name = "streamlit-octostar-utils"
8
- version = "0.5.0-dev.19"
8
+ version = "0.5.1-dev.1"
9
9
  description = ""
10
10
  license = "MIT"
11
11
  authors = ["Octostar"]
@@ -48,6 +48,65 @@ class RedisFileLock:
48
48
  return self.lock.__exit__(exc_type, exc_val, exc_tb)
49
49
 
50
50
 
51
+ def _install_queue_limit_patch(queue_limits):
52
+ """Monkey-patch kombu's Redis Channel._put for atomic queue limits.
53
+
54
+ The original _put does a bare LPUSH. The patched version wraps
55
+ limited queues in a Lua script (LLEN check + LPUSH) so the
56
+ "is there room?" test and the "enqueue" are a single atomic
57
+ Redis operation. Queues without a configured limit are
58
+ forwarded to the original _put unchanged.
59
+ """
60
+ from kombu.transport.redis import Channel
61
+ from kombu.utils.json import dumps as kombu_dumps
62
+
63
+ _ATOMIC_ENQUEUE_LUA = """
64
+ local total = 0
65
+ for i = 1, #KEYS do
66
+ total = total + redis.call('LLEN', KEYS[i])
67
+ end
68
+ if total >= tonumber(ARGV[1]) then
69
+ return 0
70
+ end
71
+ redis.call('LPUSH', KEYS[1], ARGV[2])
72
+ return 1
73
+ """
74
+
75
+ if not hasattr(Channel, "_queue_limits"):
76
+ Channel._queue_limits = {}
77
+ Channel._queue_limits.update(queue_limits)
78
+
79
+ if getattr(Channel, "_queue_limit_patched", False):
80
+ return
81
+
82
+ _original_put = Channel._put
83
+
84
+ def _put_with_limit(self, queue, message, **kwargs):
85
+ max_tasks = Channel._queue_limits.get(queue)
86
+ if max_tasks is not None:
87
+ pri = self._get_message_priority(message, reverse=False)
88
+ target_key = self._q_for_pri(queue, pri)
89
+ all_keys = [self._q_for_pri(queue, p) for p in self.priority_steps]
90
+ if target_key in all_keys:
91
+ all_keys.remove(target_key)
92
+ all_keys.insert(0, target_key)
93
+ with self.conn_or_acquire() as client:
94
+ accepted = client.eval(
95
+ _ATOMIC_ENQUEUE_LUA, len(all_keys), *all_keys,
96
+ max_tasks, kombu_dumps(message),
97
+ )
98
+ if not accepted:
99
+ from streamlit_octostar_utils.api_crafter.celery import CeleryExecutor
100
+ raise CeleryExecutor.QueueFullException(
101
+ f"Queue '{queue}' has reached its limit of {max_tasks} tasks!"
102
+ )
103
+ else:
104
+ _original_put(self, queue, message, **kwargs)
105
+
106
+ Channel._put = _put_with_limit
107
+ Channel._queue_limit_patched = True
108
+
109
+
51
110
  class CeleryQueueConfig:
52
111
  def __init__(
53
112
  self,
@@ -201,10 +260,11 @@ class CeleryExecutor(object):
201
260
  self.get_thread_pool = None
202
261
  self.set_thread_pool = None
203
262
  self.io_thread_pool = None
204
- self.queue_semaphores = {
205
- k: threading.Semaphore(v.max_tasks_in_queue) if v.max_tasks_in_queue else None
206
- for k, v in self.queue_config.items()
207
- }
263
+
264
+ # Atomic queue-depth limits
265
+ queue_limits = {k: v.max_tasks_in_queue for k, v in self.queue_config.items() if v.max_tasks_in_queue}
266
+ if queue_limits:
267
+ _install_queue_limit_patch(queue_limits)
208
268
 
209
269
  # Folder setup
210
270
  self.base_folder = Path(base_folder).resolve()
@@ -720,17 +780,6 @@ class CeleryExecutor(object):
720
780
  self.preload_on_worker_init()
721
781
  self.app.conf.dev_preload = True
722
782
 
723
- def _check_queue_llen(queue_name):
724
- if self._queue_stalled.get(queue_name, False):
725
- raise CeleryExecutor.QueueStalledException(
726
- f"Queue '{queue_name}' is stalled. Service temporarily unavailable."
727
- )
728
- if self.redis_client.llen(queue_name) >= self.queue_config[queue_name].max_tasks_in_queue:
729
- raise CeleryExecutor.QueueFullException(
730
- f"Queue '{queue_name}' has reached its limit of "
731
- f"{self.queue_config[queue_name].max_tasks_in_queue} tasks!"
732
- )
733
-
734
783
  def _write_task_data(in_folder, task_args, task_kwargs, task_id):
735
784
  serialized_data = CelerySerialized(
736
785
  folder=in_folder,
@@ -754,22 +803,12 @@ class CeleryExecutor(object):
754
803
  queue_name = getattr(task_fn, "queue", queue_name)
755
804
  queue_name = options.get("queue", queue_name)
756
805
 
757
- sem = self.queue_semaphores.get(queue_name)
758
- acquired = False
759
- if sem is not None:
760
- if not sem.acquire(blocking=False):
761
- raise CeleryExecutor.QueueFullException(
762
- f"Queue '{queue_name}' has reached its limit of "
763
- f"{self.queue_config[queue_name].max_tasks_in_queue} tasks!"
764
- )
765
- acquired = True
806
+ if self._queue_stalled.get(queue_name, False):
807
+ raise CeleryExecutor.QueueStalledException(
808
+ f"Queue '{queue_name}' is stalled. Service temporarily unavailable."
809
+ )
766
810
 
767
811
  try:
768
- if acquired:
769
- await asyncio.get_running_loop().run_in_executor(
770
- self.set_thread_pool, _check_queue_llen, queue_name
771
- )
772
-
773
812
  if part is not None:
774
813
  await self._write_task_data_with_part(
775
814
  task_id, args, kwargs, part
@@ -800,9 +839,6 @@ class CeleryExecutor(object):
800
839
  except Exception:
801
840
  pass
802
841
  raise
803
- finally:
804
- if acquired:
805
- sem.release()
806
842
  return task_id
807
843
 
808
844
  async def _write_task_data_with_part(self, task_id, args, kwargs, part):