streamlit-octostar-utils 0.5.2.dev1__tar.gz → 0.5.3__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.
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/PKG-INFO +1 -1
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/pyproject.toml +1 -1
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/api_crafter/celery.py +68 -32
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/api_crafter/nifi.py +38 -3
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/LICENSE +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/README.md +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/__init__.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/api_crafter/__init__.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/api_crafter/contents.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/api_crafter/fastapi.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/api_crafter/parallelism.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/api_crafter/parser/__init__.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/api_crafter/parser/combine_fields.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/api_crafter/parser/entities_parser.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/api_crafter/parser/generics.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/api_crafter/parser/info.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/api_crafter/parser/linkchart_functions.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/api_crafter/parser/matches.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/api_crafter/parser/parameters.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/api_crafter/parser/rules.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/api_crafter/parser/signals.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/core/__init__.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/core/dict.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/core/filetypes.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/core/threading/__init__.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/core/threading/key_queue.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/core/timestamp.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/nlp/__init__.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/nlp/custom_recognizers.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/nlp/language.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/nlp/ner.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/octostar/__init__.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/octostar/client.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/octostar/context.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/octostar/permissions.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/ontology/__init__.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/ontology/inheritance.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/ontology/relationships.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/ontology/validation.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/style/__init__.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/style/common.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/threading/__init__.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/threading/async_task_manager.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/threading/session_callback_manager.py +0 -0
- {streamlit_octostar_utils-0.5.2.dev1 → streamlit_octostar_utils-0.5.3}/streamlit_octostar_utils/threading/session_state_hot_swapper.py +0 -0
|
@@ -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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
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):
|
|
@@ -17,6 +17,7 @@ from starlette.exceptions import HTTPException as StarletteHTTPException
|
|
|
17
17
|
|
|
18
18
|
from octostar.utils.workspace import upsert_entities
|
|
19
19
|
from octostar.utils.ontology import fetch_ontology_data
|
|
20
|
+
from octostar.utils.workspace.permissions import get_permissions, PermissionLevel
|
|
20
21
|
from octostar.utils.pipeline import update_processing_status
|
|
21
22
|
|
|
22
23
|
from octostar.client import make_client
|
|
@@ -84,6 +85,7 @@ class NifiEntityModel(BaseModel):
|
|
|
84
85
|
is_temporary: bool = False
|
|
85
86
|
exception: dict = Field(default_factory=dict)
|
|
86
87
|
last_processor_name: Optional[str] = None
|
|
88
|
+
fallback_os_workspace: Optional[str] = None
|
|
87
89
|
|
|
88
90
|
class RecordModel(BaseModel):
|
|
89
91
|
model_config = ConfigDict(extra="allow")
|
|
@@ -473,6 +475,7 @@ class NifiContextManager(object):
|
|
|
473
475
|
def __init__(self, json_data, lazy_sync=True):
|
|
474
476
|
if not json_data:
|
|
475
477
|
raise ValueError("Nifi context manager received list of 0 entities")
|
|
478
|
+
self.permissions = {}
|
|
476
479
|
self.in_batches = None
|
|
477
480
|
self.out_entities = None
|
|
478
481
|
self.nonlazy_sync_ids = set()
|
|
@@ -549,6 +552,16 @@ class NifiContextManager(object):
|
|
|
549
552
|
def __enter__(self):
|
|
550
553
|
return self
|
|
551
554
|
|
|
555
|
+
def get_workspaces_permissions(self, workspace_ids):
|
|
556
|
+
permissions_to_fetch = list(set(workspace_ids).difference(set(list(self.permissions.keys()))))
|
|
557
|
+
if permissions_to_fetch:
|
|
558
|
+
permissions = get_permissions.sync(permissions_to_fetch, client=self.client)
|
|
559
|
+
self.permissions.update(permissions)
|
|
560
|
+
permissions = {}
|
|
561
|
+
for k in workspace_ids:
|
|
562
|
+
permissions[k] = self.permissions.get(k, PermissionLevel.NONE)
|
|
563
|
+
return permissions
|
|
564
|
+
|
|
552
565
|
def request_entity_sync(
|
|
553
566
|
self,
|
|
554
567
|
entity,
|
|
@@ -957,7 +970,29 @@ class NifiEntity(object):
|
|
|
957
970
|
|
|
958
971
|
@property
|
|
959
972
|
def write_os_workspace(self):
|
|
960
|
-
|
|
973
|
+
permissions = self.context.get_workspaces_permissions(
|
|
974
|
+
[
|
|
975
|
+
e
|
|
976
|
+
for e in [
|
|
977
|
+
self.record.get("os_workspace"),
|
|
978
|
+
self.request.get("fallback_os_workspace"),
|
|
979
|
+
]
|
|
980
|
+
if e
|
|
981
|
+
]
|
|
982
|
+
)
|
|
983
|
+
if (
|
|
984
|
+
self.record.get("os_workspace")
|
|
985
|
+
and (permissions.get(self.record.get("os_workspace")) or PermissionLevel.NONE) >= PermissionLevel.WRITE
|
|
986
|
+
):
|
|
987
|
+
return self.record["os_workspace"]
|
|
988
|
+
elif (
|
|
989
|
+
self.request.get("fallback_os_workspace")
|
|
990
|
+
and (permissions.get(self.request.get("fallback_os_workspace")) or PermissionLevel.NONE)
|
|
991
|
+
>= PermissionLevel.WRITE
|
|
992
|
+
):
|
|
993
|
+
return self.request["fallback_os_workspace"]
|
|
994
|
+
else:
|
|
995
|
+
return None
|
|
961
996
|
|
|
962
997
|
@property
|
|
963
998
|
def label(self):
|
|
@@ -1175,6 +1210,7 @@ class NifiEntity(object):
|
|
|
1175
1210
|
"is_temporary": True,
|
|
1176
1211
|
"exception": {},
|
|
1177
1212
|
"last_processor_name": None,
|
|
1213
|
+
"fallback_os_workspace": self.request["fallback_os_workspace"],
|
|
1178
1214
|
}
|
|
1179
1215
|
child_entity = NifiEntity(
|
|
1180
1216
|
self.context,
|
|
@@ -1400,7 +1436,6 @@ class NifiEntity(object):
|
|
|
1400
1436
|
os_entity_uid=None,
|
|
1401
1437
|
os_relationship_uid=None,
|
|
1402
1438
|
os_entity_type=FRAGMENT_ENTITY_NAME,
|
|
1403
|
-
os_parent_uid=None,
|
|
1404
1439
|
previous_fragment_uid=None,
|
|
1405
1440
|
previous_fragment_relationship_uid=None,
|
|
1406
1441
|
previous_fragment_relationship=PREVIOUS_FRAGMENT_RELATIONSHIP,
|
|
@@ -1413,7 +1448,7 @@ class NifiEntity(object):
|
|
|
1413
1448
|
fields = {
|
|
1414
1449
|
**{k: v for k, v in self.record.items() if k.startswith("fragment") and v is not None},
|
|
1415
1450
|
**fields,
|
|
1416
|
-
"os_parent_uid":
|
|
1451
|
+
"os_parent_uid": self.record["os_entity_uid"],
|
|
1417
1452
|
"source_entity_uid": source_entity_uid,
|
|
1418
1453
|
"previous_entity_uid": previous_fragment_uid,
|
|
1419
1454
|
"next_entity_uid": next_fragment_uid,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|