nv-ingest-api 2025.4.17.dev20250417__py3-none-any.whl → 2025.4.19.dev20250419__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.
Potentially problematic release.
This version of nv-ingest-api might be problematic. Click here for more details.
- nv_ingest_api/__init__.py +0 -3
- nv_ingest_api/{internal/primitives → primitives}/control_message_task.py +0 -4
- nv_ingest_api/{internal/primitives → primitives}/ingest_control_message.py +2 -5
- {nv_ingest_api-2025.4.17.dev20250417.dist-info → nv_ingest_api-2025.4.19.dev20250419.dist-info}/METADATA +1 -1
- nv_ingest_api-2025.4.19.dev20250419.dist-info/RECORD +9 -0
- {nv_ingest_api-2025.4.17.dev20250417.dist-info → nv_ingest_api-2025.4.19.dev20250419.dist-info}/WHEEL +1 -1
- nv_ingest_api/interface/__init__.py +0 -215
- nv_ingest_api/interface/extract.py +0 -972
- nv_ingest_api/interface/mutate.py +0 -154
- nv_ingest_api/interface/store.py +0 -218
- nv_ingest_api/interface/transform.py +0 -382
- nv_ingest_api/interface/utility.py +0 -200
- nv_ingest_api/internal/enums/__init__.py +0 -3
- nv_ingest_api/internal/enums/common.py +0 -494
- nv_ingest_api/internal/extract/__init__.py +0 -3
- nv_ingest_api/internal/extract/audio/__init__.py +0 -3
- nv_ingest_api/internal/extract/audio/audio_extraction.py +0 -149
- nv_ingest_api/internal/extract/docx/__init__.py +0 -5
- nv_ingest_api/internal/extract/docx/docx_extractor.py +0 -205
- nv_ingest_api/internal/extract/docx/engines/__init__.py +0 -0
- nv_ingest_api/internal/extract/docx/engines/docxreader_helpers/__init__.py +0 -3
- nv_ingest_api/internal/extract/docx/engines/docxreader_helpers/docx_helper.py +0 -122
- nv_ingest_api/internal/extract/docx/engines/docxreader_helpers/docxreader.py +0 -895
- nv_ingest_api/internal/extract/image/__init__.py +0 -3
- nv_ingest_api/internal/extract/image/chart_extractor.py +0 -353
- nv_ingest_api/internal/extract/image/image_extractor.py +0 -204
- nv_ingest_api/internal/extract/image/image_helpers/__init__.py +0 -3
- nv_ingest_api/internal/extract/image/image_helpers/common.py +0 -403
- nv_ingest_api/internal/extract/image/infographic_extractor.py +0 -253
- nv_ingest_api/internal/extract/image/table_extractor.py +0 -344
- nv_ingest_api/internal/extract/pdf/__init__.py +0 -3
- nv_ingest_api/internal/extract/pdf/engines/__init__.py +0 -19
- nv_ingest_api/internal/extract/pdf/engines/adobe.py +0 -484
- nv_ingest_api/internal/extract/pdf/engines/llama.py +0 -243
- nv_ingest_api/internal/extract/pdf/engines/nemoretriever.py +0 -597
- nv_ingest_api/internal/extract/pdf/engines/pdf_helpers/__init__.py +0 -146
- nv_ingest_api/internal/extract/pdf/engines/pdfium.py +0 -603
- nv_ingest_api/internal/extract/pdf/engines/tika.py +0 -96
- nv_ingest_api/internal/extract/pdf/engines/unstructured_io.py +0 -426
- nv_ingest_api/internal/extract/pdf/pdf_extractor.py +0 -74
- nv_ingest_api/internal/extract/pptx/__init__.py +0 -5
- nv_ingest_api/internal/extract/pptx/engines/__init__.py +0 -0
- nv_ingest_api/internal/extract/pptx/engines/pptx_helper.py +0 -799
- nv_ingest_api/internal/extract/pptx/pptx_extractor.py +0 -187
- nv_ingest_api/internal/mutate/__init__.py +0 -3
- nv_ingest_api/internal/mutate/deduplicate.py +0 -110
- nv_ingest_api/internal/mutate/filter.py +0 -133
- nv_ingest_api/internal/primitives/__init__.py +0 -0
- nv_ingest_api/internal/primitives/nim/__init__.py +0 -8
- nv_ingest_api/internal/primitives/nim/default_values.py +0 -15
- nv_ingest_api/internal/primitives/nim/model_interface/__init__.py +0 -3
- nv_ingest_api/internal/primitives/nim/model_interface/cached.py +0 -274
- nv_ingest_api/internal/primitives/nim/model_interface/decorators.py +0 -56
- nv_ingest_api/internal/primitives/nim/model_interface/deplot.py +0 -270
- nv_ingest_api/internal/primitives/nim/model_interface/helpers.py +0 -275
- nv_ingest_api/internal/primitives/nim/model_interface/nemoretriever_parse.py +0 -238
- nv_ingest_api/internal/primitives/nim/model_interface/paddle.py +0 -462
- nv_ingest_api/internal/primitives/nim/model_interface/parakeet.py +0 -367
- nv_ingest_api/internal/primitives/nim/model_interface/text_embedding.py +0 -132
- nv_ingest_api/internal/primitives/nim/model_interface/vlm.py +0 -152
- nv_ingest_api/internal/primitives/nim/model_interface/yolox.py +0 -1400
- nv_ingest_api/internal/primitives/nim/nim_client.py +0 -344
- nv_ingest_api/internal/primitives/nim/nim_model_interface.py +0 -81
- nv_ingest_api/internal/primitives/tracing/__init__.py +0 -0
- nv_ingest_api/internal/primitives/tracing/latency.py +0 -69
- nv_ingest_api/internal/primitives/tracing/logging.py +0 -96
- nv_ingest_api/internal/primitives/tracing/tagging.py +0 -197
- nv_ingest_api/internal/schemas/__init__.py +0 -3
- nv_ingest_api/internal/schemas/extract/__init__.py +0 -3
- nv_ingest_api/internal/schemas/extract/extract_audio_schema.py +0 -130
- nv_ingest_api/internal/schemas/extract/extract_chart_schema.py +0 -135
- nv_ingest_api/internal/schemas/extract/extract_docx_schema.py +0 -124
- nv_ingest_api/internal/schemas/extract/extract_image_schema.py +0 -124
- nv_ingest_api/internal/schemas/extract/extract_infographic_schema.py +0 -128
- nv_ingest_api/internal/schemas/extract/extract_pdf_schema.py +0 -218
- nv_ingest_api/internal/schemas/extract/extract_pptx_schema.py +0 -124
- nv_ingest_api/internal/schemas/extract/extract_table_schema.py +0 -129
- nv_ingest_api/internal/schemas/message_brokers/__init__.py +0 -3
- nv_ingest_api/internal/schemas/message_brokers/message_broker_client_schema.py +0 -23
- nv_ingest_api/internal/schemas/message_brokers/request_schema.py +0 -34
- nv_ingest_api/internal/schemas/message_brokers/response_schema.py +0 -19
- nv_ingest_api/internal/schemas/meta/__init__.py +0 -3
- nv_ingest_api/internal/schemas/meta/base_model_noext.py +0 -11
- nv_ingest_api/internal/schemas/meta/ingest_job_schema.py +0 -237
- nv_ingest_api/internal/schemas/meta/metadata_schema.py +0 -221
- nv_ingest_api/internal/schemas/mutate/__init__.py +0 -3
- nv_ingest_api/internal/schemas/mutate/mutate_image_dedup_schema.py +0 -16
- nv_ingest_api/internal/schemas/store/__init__.py +0 -3
- nv_ingest_api/internal/schemas/store/store_embedding_schema.py +0 -28
- nv_ingest_api/internal/schemas/store/store_image_schema.py +0 -30
- nv_ingest_api/internal/schemas/transform/__init__.py +0 -3
- nv_ingest_api/internal/schemas/transform/transform_image_caption_schema.py +0 -15
- nv_ingest_api/internal/schemas/transform/transform_image_filter_schema.py +0 -17
- nv_ingest_api/internal/schemas/transform/transform_text_embedding_schema.py +0 -25
- nv_ingest_api/internal/schemas/transform/transform_text_splitter_schema.py +0 -22
- nv_ingest_api/internal/store/__init__.py +0 -3
- nv_ingest_api/internal/store/embed_text_upload.py +0 -236
- nv_ingest_api/internal/store/image_upload.py +0 -232
- nv_ingest_api/internal/transform/__init__.py +0 -3
- nv_ingest_api/internal/transform/caption_image.py +0 -205
- nv_ingest_api/internal/transform/embed_text.py +0 -496
- nv_ingest_api/internal/transform/split_text.py +0 -157
- nv_ingest_api/util/__init__.py +0 -0
- nv_ingest_api/util/control_message/__init__.py +0 -0
- nv_ingest_api/util/control_message/validators.py +0 -47
- nv_ingest_api/util/converters/__init__.py +0 -0
- nv_ingest_api/util/converters/bytetools.py +0 -78
- nv_ingest_api/util/converters/containers.py +0 -65
- nv_ingest_api/util/converters/datetools.py +0 -90
- nv_ingest_api/util/converters/dftools.py +0 -127
- nv_ingest_api/util/converters/formats.py +0 -64
- nv_ingest_api/util/converters/type_mappings.py +0 -27
- nv_ingest_api/util/detectors/__init__.py +0 -5
- nv_ingest_api/util/detectors/language.py +0 -38
- nv_ingest_api/util/exception_handlers/__init__.py +0 -0
- nv_ingest_api/util/exception_handlers/converters.py +0 -72
- nv_ingest_api/util/exception_handlers/decorators.py +0 -223
- nv_ingest_api/util/exception_handlers/detectors.py +0 -74
- nv_ingest_api/util/exception_handlers/pdf.py +0 -116
- nv_ingest_api/util/exception_handlers/schemas.py +0 -68
- nv_ingest_api/util/image_processing/__init__.py +0 -5
- nv_ingest_api/util/image_processing/clustering.py +0 -260
- nv_ingest_api/util/image_processing/processing.py +0 -179
- nv_ingest_api/util/image_processing/table_and_chart.py +0 -449
- nv_ingest_api/util/image_processing/transforms.py +0 -407
- nv_ingest_api/util/logging/__init__.py +0 -0
- nv_ingest_api/util/logging/configuration.py +0 -31
- nv_ingest_api/util/message_brokers/__init__.py +0 -3
- nv_ingest_api/util/message_brokers/simple_message_broker/__init__.py +0 -9
- nv_ingest_api/util/message_brokers/simple_message_broker/broker.py +0 -465
- nv_ingest_api/util/message_brokers/simple_message_broker/ordered_message_queue.py +0 -71
- nv_ingest_api/util/message_brokers/simple_message_broker/simple_client.py +0 -435
- nv_ingest_api/util/metadata/__init__.py +0 -5
- nv_ingest_api/util/metadata/aggregators.py +0 -469
- nv_ingest_api/util/multi_processing/__init__.py +0 -8
- nv_ingest_api/util/multi_processing/mp_pool_singleton.py +0 -194
- nv_ingest_api/util/nim/__init__.py +0 -56
- nv_ingest_api/util/pdf/__init__.py +0 -3
- nv_ingest_api/util/pdf/pdfium.py +0 -427
- nv_ingest_api/util/schema/__init__.py +0 -0
- nv_ingest_api/util/schema/schema_validator.py +0 -10
- nv_ingest_api/util/service_clients/__init__.py +0 -3
- nv_ingest_api/util/service_clients/client_base.py +0 -72
- nv_ingest_api/util/service_clients/kafka/__init__.py +0 -3
- nv_ingest_api/util/service_clients/redis/__init__.py +0 -0
- nv_ingest_api/util/service_clients/redis/redis_client.py +0 -334
- nv_ingest_api/util/service_clients/rest/__init__.py +0 -0
- nv_ingest_api/util/service_clients/rest/rest_client.py +0 -398
- nv_ingest_api/util/string_processing/__init__.py +0 -51
- nv_ingest_api-2025.4.17.dev20250417.dist-info/RECORD +0 -152
- /nv_ingest_api/{internal → primitives}/__init__.py +0 -0
- {nv_ingest_api-2025.4.17.dev20250417.dist-info → nv_ingest_api-2025.4.19.dev20250419.dist-info}/licenses/LICENSE +0 -0
- {nv_ingest_api-2025.4.17.dev20250417.dist-info → nv_ingest_api-2025.4.19.dev20250419.dist-info}/top_level.txt +0 -0
|
@@ -1,435 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES.
|
|
2
|
-
# All rights reserved.
|
|
3
|
-
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
-
|
|
5
|
-
# NOTE: This code is duplicated from the ingest service:
|
|
6
|
-
# src/nv_ingest/util/message_brokers/simple_message_broker/simple_client.py
|
|
7
|
-
# Eventually we should move all client wrappers for the message broker into a shared library that both the ingest
|
|
8
|
-
# service and the client can use.
|
|
9
|
-
|
|
10
|
-
import socket
|
|
11
|
-
import json
|
|
12
|
-
import time
|
|
13
|
-
import logging
|
|
14
|
-
from typing import Optional
|
|
15
|
-
|
|
16
|
-
from nv_ingest_api.internal.schemas.message_brokers.response_schema import ResponseSchema
|
|
17
|
-
from nv_ingest_api.util.service_clients.client_base import MessageBrokerClientBase
|
|
18
|
-
|
|
19
|
-
logger = logging.getLogger(__name__)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class SimpleClient(MessageBrokerClientBase):
|
|
23
|
-
"""
|
|
24
|
-
A client for interfacing with SimpleMessageBroker, creating a new socket connection per request
|
|
25
|
-
to ensure thread safety and robustness. Respects timeouts for all operations.
|
|
26
|
-
"""
|
|
27
|
-
|
|
28
|
-
def __init__(
|
|
29
|
-
self,
|
|
30
|
-
host: str,
|
|
31
|
-
port: int,
|
|
32
|
-
db: int = 0,
|
|
33
|
-
max_retries: int = 3,
|
|
34
|
-
max_backoff: int = 32,
|
|
35
|
-
connection_timeout: int = 300,
|
|
36
|
-
max_pool_size: int = 128,
|
|
37
|
-
use_ssl: bool = False,
|
|
38
|
-
):
|
|
39
|
-
"""
|
|
40
|
-
Initialize the SimpleClient with configuration parameters.
|
|
41
|
-
|
|
42
|
-
Parameters
|
|
43
|
-
----------
|
|
44
|
-
host : str
|
|
45
|
-
The hostname or IP address of the broker.
|
|
46
|
-
port : int
|
|
47
|
-
The port number of the broker.
|
|
48
|
-
db : int, optional
|
|
49
|
-
The database index (default: 0).
|
|
50
|
-
max_retries : int, optional
|
|
51
|
-
Maximum number of retries for operations (default: 3).
|
|
52
|
-
max_backoff : int, optional
|
|
53
|
-
Maximum backoff time in seconds for retries (default: 32).
|
|
54
|
-
connection_timeout : int, optional
|
|
55
|
-
Timeout in seconds for establishing a connection (default: 300).
|
|
56
|
-
max_pool_size : int, optional
|
|
57
|
-
Maximum pool size for connections (default: 128).
|
|
58
|
-
use_ssl : bool, optional
|
|
59
|
-
Whether to use SSL for connections (default: False).
|
|
60
|
-
"""
|
|
61
|
-
|
|
62
|
-
self._host = host
|
|
63
|
-
self._port = port
|
|
64
|
-
self._db = db
|
|
65
|
-
self._max_retries = max_retries
|
|
66
|
-
self._max_backoff = max_backoff
|
|
67
|
-
self._max_pool_size = max_pool_size
|
|
68
|
-
self._connection_timeout = connection_timeout
|
|
69
|
-
self._use_ssl = use_ssl
|
|
70
|
-
|
|
71
|
-
def get_client(self):
|
|
72
|
-
"""
|
|
73
|
-
Retrieve the current client instance.
|
|
74
|
-
|
|
75
|
-
Returns
|
|
76
|
-
-------
|
|
77
|
-
SimpleClient
|
|
78
|
-
The current client instance.
|
|
79
|
-
"""
|
|
80
|
-
return self
|
|
81
|
-
|
|
82
|
-
def submit_message(
|
|
83
|
-
self, queue_name: str, message: str, timeout: Optional[float] = None, for_nv_ingest: bool = False
|
|
84
|
-
) -> ResponseSchema:
|
|
85
|
-
"""
|
|
86
|
-
Submit a message to the specified queue.
|
|
87
|
-
|
|
88
|
-
Parameters
|
|
89
|
-
----------
|
|
90
|
-
queue_name : str
|
|
91
|
-
The name of the queue.
|
|
92
|
-
message : str
|
|
93
|
-
The message to be submitted.
|
|
94
|
-
timeout : float, optional
|
|
95
|
-
Timeout in seconds for the operation.
|
|
96
|
-
for_nv_ingest : bool, optional
|
|
97
|
-
Indicates whether the message is for NV ingest operations.
|
|
98
|
-
|
|
99
|
-
Returns
|
|
100
|
-
-------
|
|
101
|
-
ResponseSchema
|
|
102
|
-
The response from the broker.
|
|
103
|
-
"""
|
|
104
|
-
return self._handle_push(queue_name, message, timeout, for_nv_ingest)
|
|
105
|
-
|
|
106
|
-
def fetch_message(self, queue_name: str, timeout: Optional[float] = None) -> ResponseSchema:
|
|
107
|
-
"""
|
|
108
|
-
Fetch a message from the specified queue.
|
|
109
|
-
|
|
110
|
-
Parameters
|
|
111
|
-
----------
|
|
112
|
-
queue_name : str
|
|
113
|
-
The name of the queue.
|
|
114
|
-
timeout : float, optional
|
|
115
|
-
Timeout in seconds for the operation.
|
|
116
|
-
|
|
117
|
-
Returns
|
|
118
|
-
-------
|
|
119
|
-
ResponseSchema
|
|
120
|
-
The response containing the fetched message.
|
|
121
|
-
"""
|
|
122
|
-
return self._handle_pop(queue_name, timeout)
|
|
123
|
-
|
|
124
|
-
def ping(self) -> ResponseSchema:
|
|
125
|
-
"""
|
|
126
|
-
Ping the broker to check connectivity.
|
|
127
|
-
|
|
128
|
-
Returns
|
|
129
|
-
-------
|
|
130
|
-
ResponseSchema
|
|
131
|
-
The response indicating the success of the ping operation.
|
|
132
|
-
"""
|
|
133
|
-
command = {"command": "PING"}
|
|
134
|
-
return self._execute_simple_command(command)
|
|
135
|
-
|
|
136
|
-
def size(self, queue_name: str) -> ResponseSchema:
|
|
137
|
-
"""
|
|
138
|
-
Fetch the size of the specified queue.
|
|
139
|
-
|
|
140
|
-
Parameters
|
|
141
|
-
----------
|
|
142
|
-
queue_name : str
|
|
143
|
-
The name of the queue.
|
|
144
|
-
|
|
145
|
-
Returns
|
|
146
|
-
-------
|
|
147
|
-
ResponseSchema
|
|
148
|
-
The response containing the queue size.
|
|
149
|
-
"""
|
|
150
|
-
command = {"command": "SIZE", "queue_name": queue_name}
|
|
151
|
-
return self._execute_simple_command(command)
|
|
152
|
-
|
|
153
|
-
def _handle_push(
|
|
154
|
-
self, queue_name: str, message: str, timeout: Optional[float], for_nv_ingest: bool
|
|
155
|
-
) -> ResponseSchema:
|
|
156
|
-
"""
|
|
157
|
-
Push a message to the queue with optional timeout.
|
|
158
|
-
|
|
159
|
-
Parameters
|
|
160
|
-
----------
|
|
161
|
-
queue_name : str
|
|
162
|
-
The name of the queue.
|
|
163
|
-
message : str
|
|
164
|
-
The message to push.
|
|
165
|
-
timeout : float, optional
|
|
166
|
-
Timeout in seconds for the operation.
|
|
167
|
-
for_nv_ingest : bool
|
|
168
|
-
Indicates whether the message is for NV ingest operations.
|
|
169
|
-
|
|
170
|
-
Returns
|
|
171
|
-
-------
|
|
172
|
-
ResponseSchema
|
|
173
|
-
The response from the broker.
|
|
174
|
-
"""
|
|
175
|
-
|
|
176
|
-
if not queue_name or not isinstance(queue_name, str):
|
|
177
|
-
return ResponseSchema(response_code=1, response_reason="Invalid queue name.")
|
|
178
|
-
if not message or not isinstance(message, str):
|
|
179
|
-
return ResponseSchema(response_code=1, response_reason="Invalid message.")
|
|
180
|
-
|
|
181
|
-
if for_nv_ingest:
|
|
182
|
-
command = {"command": "PUSH_FOR_NV_INGEST", "queue_name": queue_name, "message": message}
|
|
183
|
-
else:
|
|
184
|
-
command = {"command": "PUSH", "queue_name": queue_name, "message": message}
|
|
185
|
-
|
|
186
|
-
if timeout is not None:
|
|
187
|
-
command["timeout"] = timeout
|
|
188
|
-
|
|
189
|
-
start_time = time.time()
|
|
190
|
-
while True:
|
|
191
|
-
elapsed = time.time() - start_time
|
|
192
|
-
remaining_timeout = (timeout - elapsed) if (timeout is not None) else None
|
|
193
|
-
if (remaining_timeout is not None) and (remaining_timeout <= 0):
|
|
194
|
-
return ResponseSchema(response_code=1, response_reason="PUSH operation timed out.")
|
|
195
|
-
|
|
196
|
-
try:
|
|
197
|
-
with socket.create_connection((self._host, self._port), timeout=self._connection_timeout) as sock:
|
|
198
|
-
self._send(sock, json.dumps(command).encode("utf-8"))
|
|
199
|
-
# Receive initial response with transaction ID
|
|
200
|
-
response_data = self._recv(sock)
|
|
201
|
-
response = json.loads(response_data)
|
|
202
|
-
|
|
203
|
-
if response.get("response_code") != 0:
|
|
204
|
-
if (
|
|
205
|
-
response.get("response_reason") == "Queue is full"
|
|
206
|
-
or response.get("response_reason") == "Queue is not available"
|
|
207
|
-
):
|
|
208
|
-
time.sleep(0.5)
|
|
209
|
-
continue
|
|
210
|
-
else:
|
|
211
|
-
return ResponseSchema(**response)
|
|
212
|
-
|
|
213
|
-
if "transaction_id" not in response:
|
|
214
|
-
error_msg = "No transaction_id in response."
|
|
215
|
-
logger.error(error_msg)
|
|
216
|
-
|
|
217
|
-
return ResponseSchema(response_code=1, response_reason=error_msg)
|
|
218
|
-
|
|
219
|
-
transaction_id = response["transaction_id"]
|
|
220
|
-
|
|
221
|
-
# Send ACK
|
|
222
|
-
ack_data = json.dumps({"transaction_id": transaction_id, "ack": True}).encode("utf-8")
|
|
223
|
-
self._send(sock, ack_data)
|
|
224
|
-
|
|
225
|
-
# Receive final response
|
|
226
|
-
final_response_data = self._recv(sock)
|
|
227
|
-
final_response = json.loads(final_response_data)
|
|
228
|
-
|
|
229
|
-
return ResponseSchema(**final_response)
|
|
230
|
-
|
|
231
|
-
except (ConnectionError, socket.error, BrokenPipeError):
|
|
232
|
-
pass
|
|
233
|
-
except json.JSONDecodeError:
|
|
234
|
-
return ResponseSchema(response_code=1, response_reason="Invalid JSON response from server.")
|
|
235
|
-
except Exception as e:
|
|
236
|
-
return ResponseSchema(response_code=1, response_reason=str(e))
|
|
237
|
-
|
|
238
|
-
time.sleep(0.5) # Backoff delay before retry
|
|
239
|
-
|
|
240
|
-
def _handle_pop(self, queue_name: str, timeout: Optional[float]) -> ResponseSchema:
|
|
241
|
-
"""
|
|
242
|
-
Pop a message from the queue with optional timeout.
|
|
243
|
-
|
|
244
|
-
Parameters
|
|
245
|
-
----------
|
|
246
|
-
queue_name : str
|
|
247
|
-
The name of the queue.
|
|
248
|
-
timeout : float, optional
|
|
249
|
-
Timeout in seconds for the operation.
|
|
250
|
-
|
|
251
|
-
Returns
|
|
252
|
-
-------
|
|
253
|
-
ResponseSchema
|
|
254
|
-
The response containing the popped message.
|
|
255
|
-
"""
|
|
256
|
-
|
|
257
|
-
if not queue_name or not isinstance(queue_name, str):
|
|
258
|
-
return ResponseSchema(response_code=1, response_reason="Invalid queue name.")
|
|
259
|
-
|
|
260
|
-
command = {"command": "POP", "queue_name": queue_name}
|
|
261
|
-
if timeout is not None:
|
|
262
|
-
command["timeout"] = timeout
|
|
263
|
-
|
|
264
|
-
start_time = time.time()
|
|
265
|
-
while True:
|
|
266
|
-
elapsed = time.time() - start_time
|
|
267
|
-
remaining_timeout = timeout - elapsed if timeout else None
|
|
268
|
-
if remaining_timeout is not None and remaining_timeout <= 0:
|
|
269
|
-
return ResponseSchema(response_code=1, response_reason="POP operation timed out.")
|
|
270
|
-
|
|
271
|
-
try:
|
|
272
|
-
with socket.create_connection((self._host, self._port), timeout=self._connection_timeout) as sock:
|
|
273
|
-
self._send(sock, json.dumps(command).encode("utf-8"))
|
|
274
|
-
# Receive initial response with transaction ID and message
|
|
275
|
-
response_data = self._recv(sock)
|
|
276
|
-
response = json.loads(response_data)
|
|
277
|
-
|
|
278
|
-
if response.get("response_code") != 0:
|
|
279
|
-
if response.get("response_reason") == "Queue is empty":
|
|
280
|
-
time.sleep(0.1)
|
|
281
|
-
continue
|
|
282
|
-
else:
|
|
283
|
-
return ResponseSchema(**response)
|
|
284
|
-
|
|
285
|
-
if "transaction_id" not in response:
|
|
286
|
-
error_msg = "No transaction_id in response."
|
|
287
|
-
|
|
288
|
-
return ResponseSchema(response_code=1, response_reason=error_msg)
|
|
289
|
-
|
|
290
|
-
transaction_id = response["transaction_id"]
|
|
291
|
-
message = response.get("response")
|
|
292
|
-
|
|
293
|
-
# Send ACK
|
|
294
|
-
ack_data = json.dumps({"transaction_id": transaction_id, "ack": True}).encode("utf-8")
|
|
295
|
-
self._send(sock, ack_data)
|
|
296
|
-
|
|
297
|
-
# Receive final response
|
|
298
|
-
final_response_data = self._recv(sock)
|
|
299
|
-
final_response = json.loads(final_response_data)
|
|
300
|
-
|
|
301
|
-
if final_response.get("response_code") == 0:
|
|
302
|
-
return ResponseSchema(response_code=0, response=message, transaction_id=transaction_id)
|
|
303
|
-
else:
|
|
304
|
-
return ResponseSchema(**final_response)
|
|
305
|
-
|
|
306
|
-
except (ConnectionError, socket.error, BrokenPipeError):
|
|
307
|
-
pass
|
|
308
|
-
except json.JSONDecodeError:
|
|
309
|
-
return ResponseSchema(response_code=1, response_reason="Invalid JSON response from server.")
|
|
310
|
-
except Exception as e:
|
|
311
|
-
return ResponseSchema(response_code=1, response_reason=str(e))
|
|
312
|
-
|
|
313
|
-
time.sleep(0.1) # Backoff delay before retry
|
|
314
|
-
|
|
315
|
-
def _execute_simple_command(self, command: dict) -> ResponseSchema:
|
|
316
|
-
"""
|
|
317
|
-
Execute a simple command on the broker and process the response.
|
|
318
|
-
|
|
319
|
-
Parameters
|
|
320
|
-
----------
|
|
321
|
-
command : dict
|
|
322
|
-
The command to execute.
|
|
323
|
-
|
|
324
|
-
Returns
|
|
325
|
-
-------
|
|
326
|
-
ResponseSchema
|
|
327
|
-
The response from the broker.
|
|
328
|
-
"""
|
|
329
|
-
|
|
330
|
-
if isinstance(command, dict):
|
|
331
|
-
data = json.dumps(command).encode("utf-8")
|
|
332
|
-
elif isinstance(command, str):
|
|
333
|
-
data = command.encode("utf-8")
|
|
334
|
-
|
|
335
|
-
try:
|
|
336
|
-
with socket.create_connection((self._host, self._port), timeout=self._connection_timeout) as sock:
|
|
337
|
-
self._send(sock, data)
|
|
338
|
-
response_data = self._recv(sock)
|
|
339
|
-
response = json.loads(response_data)
|
|
340
|
-
return ResponseSchema(**response)
|
|
341
|
-
except (ConnectionError, socket.error, BrokenPipeError) as e:
|
|
342
|
-
return ResponseSchema(response_code=1, response_reason=f"Connection error: {e}")
|
|
343
|
-
except json.JSONDecodeError:
|
|
344
|
-
return ResponseSchema(response_code=1, response_reason="Invalid JSON response from server.")
|
|
345
|
-
except Exception as e:
|
|
346
|
-
return ResponseSchema(response_code=1, response_reason=str(e))
|
|
347
|
-
|
|
348
|
-
def _send(self, sock: socket.socket, data: bytes) -> None:
|
|
349
|
-
"""
|
|
350
|
-
Send data over a socket connection with a length header.
|
|
351
|
-
|
|
352
|
-
Parameters
|
|
353
|
-
----------
|
|
354
|
-
sock : socket.socket
|
|
355
|
-
The socket connection.
|
|
356
|
-
data : bytes
|
|
357
|
-
The data to send.
|
|
358
|
-
|
|
359
|
-
Raises
|
|
360
|
-
------
|
|
361
|
-
ConnectionError
|
|
362
|
-
If sending data fails.
|
|
363
|
-
"""
|
|
364
|
-
|
|
365
|
-
total_length = len(data)
|
|
366
|
-
if total_length == 0:
|
|
367
|
-
raise ValueError("Cannot send an empty message.")
|
|
368
|
-
|
|
369
|
-
try:
|
|
370
|
-
sock.sendall(total_length.to_bytes(8, "big"))
|
|
371
|
-
sock.sendall(data)
|
|
372
|
-
except (socket.error, BrokenPipeError):
|
|
373
|
-
raise ConnectionError("Failed to send data.")
|
|
374
|
-
|
|
375
|
-
def _recv(self, sock: socket.socket) -> str:
|
|
376
|
-
"""
|
|
377
|
-
Receive data from a socket connection based on a length header.
|
|
378
|
-
|
|
379
|
-
Parameters
|
|
380
|
-
----------
|
|
381
|
-
sock : socket.socket
|
|
382
|
-
The socket connection.
|
|
383
|
-
|
|
384
|
-
Returns
|
|
385
|
-
-------
|
|
386
|
-
str
|
|
387
|
-
The received data as a string.
|
|
388
|
-
|
|
389
|
-
Raises
|
|
390
|
-
------
|
|
391
|
-
ConnectionError
|
|
392
|
-
If receiving data fails.
|
|
393
|
-
"""
|
|
394
|
-
|
|
395
|
-
try:
|
|
396
|
-
length_header = self._recv_exact(sock, 8)
|
|
397
|
-
if not length_header:
|
|
398
|
-
raise ConnectionError("Incomplete length header received.")
|
|
399
|
-
total_length = int.from_bytes(length_header, "big")
|
|
400
|
-
data_bytes = self._recv_exact(sock, total_length)
|
|
401
|
-
if not data_bytes:
|
|
402
|
-
raise ConnectionError("Incomplete message received.")
|
|
403
|
-
return data_bytes.decode("utf-8")
|
|
404
|
-
except (socket.error, BrokenPipeError, ConnectionError):
|
|
405
|
-
raise ConnectionError("Failed to receive data.")
|
|
406
|
-
|
|
407
|
-
def _recv_exact(self, sock: socket.socket, num_bytes: int) -> Optional[bytes]:
|
|
408
|
-
"""
|
|
409
|
-
Receive an exact number of bytes from a socket connection.
|
|
410
|
-
|
|
411
|
-
Parameters
|
|
412
|
-
----------
|
|
413
|
-
sock : socket.socket
|
|
414
|
-
The socket connection.
|
|
415
|
-
num_bytes : int
|
|
416
|
-
The number of bytes to receive.
|
|
417
|
-
|
|
418
|
-
Returns
|
|
419
|
-
-------
|
|
420
|
-
Optional[bytes]
|
|
421
|
-
The received bytes, or None if the connection is closed.
|
|
422
|
-
"""
|
|
423
|
-
|
|
424
|
-
data = bytearray()
|
|
425
|
-
while len(data) < num_bytes:
|
|
426
|
-
try:
|
|
427
|
-
packet = sock.recv(num_bytes - len(data))
|
|
428
|
-
if not packet:
|
|
429
|
-
return None
|
|
430
|
-
data.extend(packet)
|
|
431
|
-
except socket.timeout:
|
|
432
|
-
return None
|
|
433
|
-
except Exception:
|
|
434
|
-
return None
|
|
435
|
-
return bytes(data)
|