fastprocesses 0.19.3__tar.gz → 0.19.4__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.
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/PKG-INFO +2 -2
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/README.md +1 -1
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/pyproject.toml +1 -1
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/api/manager.py +20 -0
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/common.py +1 -0
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/core/base_process.py +1 -1
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/core/config.py +4 -0
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/processes/process_registry.py +1 -1
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/worker/celery_app.py +30 -11
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/AUTHORS.md +0 -0
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/__init__.py +0 -0
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/api/__init__.py +0 -0
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/api/router.py +0 -0
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/api/server.py +0 -0
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/core/__init__.py +0 -0
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/core/cache.py +0 -0
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/core/exceptions.py +0 -0
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/core/logging.py +0 -0
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/core/models.py +0 -0
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/core/redis_connection.py +0 -0
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/core/types.py +0 -0
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/processes/__init__.py +0 -0
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/py.typed +0 -0
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/static/style.css +0 -0
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/templates/landing.html +0 -0
- {fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/worker/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: fastprocesses
|
|
3
|
-
Version: 0.19.
|
|
3
|
+
Version: 0.19.4
|
|
4
4
|
Summary: A library to create a FastAPI-based OGC API Processes wrapper around existing projects.
|
|
5
5
|
Author: Stefan Schuhart
|
|
6
6
|
Author-email: stefan.schuhart@gv.hamburg.de
|
|
@@ -38,7 +38,7 @@ A library to create a FastAPI-based OGC API Processes wrapper around existing pr
|
|
|
38
38
|
|
|
39
39
|
AI helped to create this code.
|
|
40
40
|
|
|
41
|
-
## Version: 0.19.
|
|
41
|
+
## Version: 0.19.4
|
|
42
42
|
|
|
43
43
|
### Description
|
|
44
44
|
|
|
@@ -79,7 +79,7 @@ exclude = [
|
|
|
79
79
|
|
|
80
80
|
[tool.poetry]
|
|
81
81
|
name = "fastprocesses"
|
|
82
|
-
version = "0.19.
|
|
82
|
+
version = "0.19.4"
|
|
83
83
|
description = "A library to create a FastAPI-based OGC API Processes wrapper around existing projects."
|
|
84
84
|
authors = ["Stefan Schuhart <stefan.schuhart@gv.hamburg.de>"]
|
|
85
85
|
readme = "README.md"
|
|
@@ -94,6 +94,12 @@ class AsyncExecutionStrategy(ExecutionStrategy):
|
|
|
94
94
|
)
|
|
95
95
|
raise BrokerUnavailableError(str(exc)) from exc
|
|
96
96
|
send_elapsed = time.monotonic() - send_start
|
|
97
|
+
logger.info(
|
|
98
|
+
"Task queued: process_id={}, job_id={}, mode=async, send_time={:.3f}s",
|
|
99
|
+
process_id,
|
|
100
|
+
task.id,
|
|
101
|
+
send_elapsed,
|
|
102
|
+
)
|
|
97
103
|
if send_elapsed > 1.0:
|
|
98
104
|
logger.warning(
|
|
99
105
|
"Celery async send_task for process_id={} took {:.2f}s",
|
|
@@ -155,6 +161,13 @@ class AsyncExecutionStrategy(ExecutionStrategy):
|
|
|
155
161
|
)
|
|
156
162
|
raise BrokerUnavailableError(str(exc)) from exc
|
|
157
163
|
send_elapsed = time.monotonic() - send_start
|
|
164
|
+
logger.info(
|
|
165
|
+
"Task queued: process_id={}, job_id={},"
|
|
166
|
+
" mode=async (raw), send_time={:.3f}s",
|
|
167
|
+
process_id,
|
|
168
|
+
task.id,
|
|
169
|
+
send_elapsed,
|
|
170
|
+
)
|
|
158
171
|
if send_elapsed > 1.0:
|
|
159
172
|
logger.warning(
|
|
160
173
|
"Celery async send_task for process_id={} took {:.2f}s",
|
|
@@ -218,6 +231,13 @@ class SyncExecutionStrategy(ExecutionStrategy):
|
|
|
218
231
|
exc,
|
|
219
232
|
)
|
|
220
233
|
raise BrokerUnavailableError(str(exc)) from exc
|
|
234
|
+
send_elapsed_sync = time.monotonic() - send_start
|
|
235
|
+
logger.info(
|
|
236
|
+
"Task queued: process_id={}, job_id={}, mode=sync, send_time={:.3f}s",
|
|
237
|
+
process_id,
|
|
238
|
+
task.id,
|
|
239
|
+
send_elapsed_sync,
|
|
240
|
+
)
|
|
221
241
|
send_elapsed = time.monotonic() - send_start
|
|
222
242
|
if send_elapsed > 1.0:
|
|
223
243
|
logger.warning(
|
|
@@ -255,7 +255,7 @@ class BaseProcess(ABC):
|
|
|
255
255
|
|
|
256
256
|
# First, check all provided inputs
|
|
257
257
|
for input_name, input_value in inputs.items():
|
|
258
|
-
logger.info("Validating input '
|
|
258
|
+
logger.info("Validating input '{}'", input_name)
|
|
259
259
|
|
|
260
260
|
if input_name not in required_inputs:
|
|
261
261
|
raise ValueError(
|
|
@@ -74,6 +74,10 @@ class OGCProcessesSettings(BaseSettings):
|
|
|
74
74
|
FP_CELERY_RESULTS_TTL_DAYS: int = 365
|
|
75
75
|
FP_CELERY_TASK_TLIMIT_HARD: int = 900 # seconds
|
|
76
76
|
FP_CELERY_TASK_TLIMIT_SOFT: int = 600 # seconds
|
|
77
|
+
FP_CELERY_QUEUE: str = Field(
|
|
78
|
+
default="celery",
|
|
79
|
+
description="Celery queue name for task routing. Must match the worker -Q flag.",
|
|
80
|
+
)
|
|
77
81
|
FP_CELERY_JOB_MODE: bool = Field(
|
|
78
82
|
default=False,
|
|
79
83
|
description="Enable job mode for graceful shutdown after task completion"
|
{fastprocesses-0.19.3 → fastprocesses-0.19.4}/src/fastprocesses/processes/process_registry.py
RENAMED
|
@@ -15,7 +15,7 @@ class ProcessRegistry:
|
|
|
15
15
|
"""Manages the registration and retrieval of available processs (processes)."""
|
|
16
16
|
|
|
17
17
|
def __init__(self, redis_connection: RedisConnection | None = None):
|
|
18
|
-
self.registry_key = "process_registry"
|
|
18
|
+
self.registry_key = f"process_registry:{settings.FP_CELERY_QUEUE}"
|
|
19
19
|
if redis_connection is None:
|
|
20
20
|
redis_connection = RedisConnection(str(settings.results_cache.connection))
|
|
21
21
|
self.redis_connection = redis_connection
|
|
@@ -3,6 +3,8 @@ import asyncio
|
|
|
3
3
|
import inspect
|
|
4
4
|
import json
|
|
5
5
|
import signal
|
|
6
|
+
import time
|
|
7
|
+
import traceback
|
|
6
8
|
from datetime import datetime, timezone
|
|
7
9
|
from typing import Any, Dict, cast
|
|
8
10
|
|
|
@@ -581,12 +583,16 @@ def execute_process(self, process_id: str, serialized_data: str | bytes):
|
|
|
581
583
|
serialized_data_str = bytes(serialized_data).decode("utf-8")
|
|
582
584
|
data: dict = json.loads(serialized_data_str)
|
|
583
585
|
|
|
584
|
-
logger.info(f"Executing process {process_id} with data {serialized_data[:80]}")
|
|
585
586
|
job_id = self.request.id # Get the task/job ID
|
|
587
|
+
task_start = time.monotonic()
|
|
588
|
+
logger.info(
|
|
589
|
+
"Worker picked up task: process_id={}, job_id={}",
|
|
590
|
+
process_id,
|
|
591
|
+
job_id,
|
|
592
|
+
)
|
|
586
593
|
|
|
587
594
|
# First: Get the process
|
|
588
595
|
try:
|
|
589
|
-
logger.info(f"Worker retrieving process {process_id} from registry")
|
|
590
596
|
process = get_process_registry().get_process(process_id)
|
|
591
597
|
except ValueError as e:
|
|
592
598
|
job_status = JobStatusCode.FAILED
|
|
@@ -602,7 +608,6 @@ def execute_process(self, process_id: str, serialized_data: str | bytes):
|
|
|
602
608
|
|
|
603
609
|
# Second: validate wire-format inputs against the process description schema
|
|
604
610
|
try:
|
|
605
|
-
logger.info(f"Worker validating inputs for process {process_id}")
|
|
606
611
|
update_job_status(
|
|
607
612
|
job_id,
|
|
608
613
|
0,
|
|
@@ -651,7 +656,6 @@ def execute_process(self, process_id: str, serialized_data: str | bytes):
|
|
|
651
656
|
|
|
652
657
|
# Fourth: Execute the process
|
|
653
658
|
try:
|
|
654
|
-
logger.info("Worker executing process {}", process_id)
|
|
655
659
|
job_status = JobStatusCode.RUNNING
|
|
656
660
|
# BUG: if redis returns no job_status, this fails and creates a ValueError too
|
|
657
661
|
update_job_status(
|
|
@@ -701,21 +705,33 @@ def execute_process(self, process_id: str, serialized_data: str | bytes):
|
|
|
701
705
|
except Exception as e:
|
|
702
706
|
# Update job with error status
|
|
703
707
|
job_status = JobStatusCode.FAILED
|
|
708
|
+
task_elapsed = time.monotonic() - task_start
|
|
704
709
|
|
|
705
710
|
# decide if its a validation error or a general error
|
|
706
711
|
if isinstance(e, ValueError) or isinstance(e, ValidationError):
|
|
707
|
-
logger.error(
|
|
712
|
+
logger.error(
|
|
713
|
+
"Task failed: process_id={}, job_id={}, duration={:.2f}s, reason={}",
|
|
714
|
+
process_id,
|
|
715
|
+
job_id,
|
|
716
|
+
task_elapsed,
|
|
717
|
+
e,
|
|
718
|
+
)
|
|
708
719
|
job_message = e
|
|
709
720
|
raise e
|
|
710
721
|
|
|
711
722
|
# Log the full traceback server-side only — never expose internal
|
|
712
723
|
# class names, source lines, or line numbers to the client.
|
|
713
|
-
logger.
|
|
714
|
-
"
|
|
724
|
+
logger.error(
|
|
725
|
+
"Task failed: process_id={}, job_id={}, duration={:.2f}s, reason={}",
|
|
715
726
|
process_id,
|
|
716
727
|
job_id,
|
|
728
|
+
task_elapsed,
|
|
717
729
|
e,
|
|
718
|
-
|
|
730
|
+
)
|
|
731
|
+
logger.debug(
|
|
732
|
+
"Full traceback for job {}:\n{}",
|
|
733
|
+
job_id,
|
|
734
|
+
traceback.format_exc(),
|
|
719
735
|
)
|
|
720
736
|
|
|
721
737
|
job_message = (
|
|
@@ -731,10 +747,13 @@ def execute_process(self, process_id: str, serialized_data: str | bytes):
|
|
|
731
747
|
return None
|
|
732
748
|
|
|
733
749
|
if result:
|
|
734
|
-
|
|
750
|
+
task_elapsed = time.monotonic() - task_start
|
|
735
751
|
logger.info(
|
|
736
|
-
|
|
737
|
-
|
|
752
|
+
"Task completed: process_id={}, job_id={},"
|
|
753
|
+
" duration={:.2f}s",
|
|
754
|
+
process_id,
|
|
755
|
+
job_id,
|
|
756
|
+
task_elapsed,
|
|
738
757
|
)
|
|
739
758
|
job_status = JobStatusCode.SUCCESSFUL
|
|
740
759
|
|
|
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
|