fastprocesses 0.22.4__tar.gz → 0.22.6__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.22.4 → fastprocesses-0.22.6}/PKG-INFO +2 -2
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/README.md +1 -1
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/pyproject.toml +1 -1
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/api/manager.py +2 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/core/config.py +7 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/worker/celery_app.py +6 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/worker/chord_tasks.py +72 -19
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/worker/pipeline.py +21 -11
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/AUTHORS.md +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/__init__.py +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/api/__init__.py +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/api/router.py +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/api/server.py +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/common.py +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/core/__init__.py +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/core/base_process.py +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/core/cache.py +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/core/exceptions.py +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/core/logging.py +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/core/models.py +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/core/output_protocol.py +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/core/output_schema_resolver.py +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/core/outputs_handler.py +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/core/redis_connection.py +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/core/types.py +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/processes/__init__.py +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/processes/process_registry.py +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/py.typed +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/static/style.css +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/templates/landing.html +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/worker/__init__.py +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/worker/executors.py +0 -0
- {fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/worker/job_status.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastprocesses
|
|
3
|
-
Version: 0.22.
|
|
3
|
+
Version: 0.22.6
|
|
4
4
|
Summary: A library to create a FastAPI-based OGC API Processes wrapper around existing projects.
|
|
5
5
|
License-File: AUTHORS.md
|
|
6
6
|
Author: Stefan Schuhart
|
|
@@ -39,7 +39,7 @@ A library to create a FastAPI-based OGC API Processes wrapper around existing pr
|
|
|
39
39
|
|
|
40
40
|
AI helped to create this code.
|
|
41
41
|
|
|
42
|
-
## Version: 0.22.
|
|
42
|
+
## Version: 0.22.6
|
|
43
43
|
|
|
44
44
|
### Description
|
|
45
45
|
|
|
@@ -79,7 +79,7 @@ exclude = [
|
|
|
79
79
|
|
|
80
80
|
[tool.poetry]
|
|
81
81
|
name = "fastprocesses"
|
|
82
|
-
version = "0.22.
|
|
82
|
+
version = "0.22.6"
|
|
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"
|
|
@@ -116,6 +116,7 @@ class AsyncExecutionStrategy(ExecutionStrategy):
|
|
|
116
116
|
"processID": process_id,
|
|
117
117
|
"created": datetime.now(timezone.utc),
|
|
118
118
|
"progress": 0,
|
|
119
|
+
"message": "Waiting for a worker to pick up the task.",
|
|
119
120
|
"links": [
|
|
120
121
|
Link.model_validate(
|
|
121
122
|
{
|
|
@@ -183,6 +184,7 @@ class AsyncExecutionStrategy(ExecutionStrategy):
|
|
|
183
184
|
"processID": process_id,
|
|
184
185
|
"created": datetime.now(timezone.utc),
|
|
185
186
|
"progress": 0,
|
|
187
|
+
"message": "Waiting for a worker to pick up the task.",
|
|
186
188
|
"links": [
|
|
187
189
|
Link.model_validate(
|
|
188
190
|
{
|
|
@@ -128,6 +128,13 @@ class OGCProcessesSettings(BaseSettings):
|
|
|
128
128
|
default=10,
|
|
129
129
|
description="Timeout in seconds for synchronous execution waiting for result."
|
|
130
130
|
)
|
|
131
|
+
FP_SKIP_INPUT_VALIDATION: bool = Field(
|
|
132
|
+
default=False,
|
|
133
|
+
description=(
|
|
134
|
+
"Skip JSON-Schema input validation on the worker. "
|
|
135
|
+
"Intended for debugging only — never enable in production."
|
|
136
|
+
),
|
|
137
|
+
)
|
|
131
138
|
FP_LOG_LEVEL: str = Field(
|
|
132
139
|
default="INFO",
|
|
133
140
|
description=(
|
|
@@ -89,6 +89,12 @@ def execute_process(self, process_id: str, serialized_data: str | bytes):
|
|
|
89
89
|
process_id,
|
|
90
90
|
job_id,
|
|
91
91
|
)
|
|
92
|
+
update_job_status(
|
|
93
|
+
job_id,
|
|
94
|
+
0,
|
|
95
|
+
"Worker started, task picked up.",
|
|
96
|
+
process_id=process_id,
|
|
97
|
+
)
|
|
92
98
|
|
|
93
99
|
# Check cache before running the pipeline — avoids re-execution for
|
|
94
100
|
# identical inputs (especially important for parallel/scatter processes
|
|
@@ -72,10 +72,13 @@ def execute_parallel_item(
|
|
|
72
72
|
"""
|
|
73
73
|
item = temp_result_cache.get(item_key)
|
|
74
74
|
if item is None:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
"
|
|
75
|
+
logger.error(
|
|
76
|
+
"Claim-check data not found for key '{}' (job {}). "
|
|
77
|
+
"Cache entry may have expired or been evicted.",
|
|
78
|
+
item_key,
|
|
79
|
+
job_id,
|
|
78
80
|
)
|
|
81
|
+
return {"__error__": f"Claim-check data not found in cache for key '{item_key}'"}
|
|
79
82
|
try:
|
|
80
83
|
process = cast(
|
|
81
84
|
BaseParallelProcess,
|
|
@@ -99,14 +102,14 @@ def execute_parallel_item(
|
|
|
99
102
|
_increment_and_report_progress(job_id, total)
|
|
100
103
|
return {"__claim_check__": rkey}
|
|
101
104
|
except Exception as exc:
|
|
102
|
-
|
|
105
|
+
logger.error(
|
|
106
|
+
"Parallel subtask failed for process {} (job {}): {}",
|
|
107
|
+
process_id,
|
|
103
108
|
job_id,
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
JobStatusCode.FAILED,
|
|
107
|
-
process_id=process_id,
|
|
109
|
+
exc,
|
|
110
|
+
exc_info=True,
|
|
108
111
|
)
|
|
109
|
-
|
|
112
|
+
return {"__error__": str(exc)}
|
|
110
113
|
|
|
111
114
|
|
|
112
115
|
def _run_parallel(
|
|
@@ -168,6 +171,29 @@ def finalize_parallel(
|
|
|
168
171
|
actual results, caches the final output, and updates the job status.
|
|
169
172
|
"""
|
|
170
173
|
process = cast(BaseParallelProcess, get_process_registry().get_process(process_id))
|
|
174
|
+
|
|
175
|
+
errors = [r for r in sub_results if isinstance(r, dict) and "__error__" in r]
|
|
176
|
+
if errors:
|
|
177
|
+
error_msg = errors[0]["__error__"]
|
|
178
|
+
update_job_status(
|
|
179
|
+
job_id,
|
|
180
|
+
0,
|
|
181
|
+
f"A parallel subtask failed: {error_msg}",
|
|
182
|
+
JobStatusCode.FAILED,
|
|
183
|
+
)
|
|
184
|
+
_cleanup_progress_counter(job_id)
|
|
185
|
+
temp_result_cache.delete(meta_key)
|
|
186
|
+
logger.error(
|
|
187
|
+
"Parallel chord failed for process {} (job {}): {}/{} subtask(s) failed. "
|
|
188
|
+
"First error: {}",
|
|
189
|
+
process_id,
|
|
190
|
+
job_id,
|
|
191
|
+
len(errors),
|
|
192
|
+
len(sub_results),
|
|
193
|
+
error_msg,
|
|
194
|
+
)
|
|
195
|
+
return None
|
|
196
|
+
|
|
171
197
|
try:
|
|
172
198
|
actual_results: list[dict] = []
|
|
173
199
|
for result_ref in sub_results:
|
|
@@ -242,10 +268,13 @@ def execute_scatter_step(
|
|
|
242
268
|
"""
|
|
243
269
|
payload = temp_result_cache.get(payload_key)
|
|
244
270
|
if payload is None:
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
"
|
|
271
|
+
logger.error(
|
|
272
|
+
"Claim-check payload not found for key '{}' (job {}). "
|
|
273
|
+
"Cache entry may have expired or been evicted.",
|
|
274
|
+
payload_key,
|
|
275
|
+
job_id,
|
|
248
276
|
)
|
|
277
|
+
return {"__error__": f"Claim-check payload not found in cache for key '{payload_key}'"}
|
|
249
278
|
data: dict = payload["step_input"]
|
|
250
279
|
try:
|
|
251
280
|
process = cast(
|
|
@@ -277,14 +306,15 @@ def execute_scatter_step(
|
|
|
277
306
|
_record_scatter_step_done(job_id, step_name, total)
|
|
278
307
|
return {"__claim_check__": rkey}
|
|
279
308
|
except Exception as exc:
|
|
280
|
-
|
|
309
|
+
logger.error(
|
|
310
|
+
"Scatter step '{}' failed for process {} (job {}): {}",
|
|
311
|
+
step_name,
|
|
312
|
+
process_id,
|
|
281
313
|
job_id,
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
JobStatusCode.FAILED,
|
|
285
|
-
process_id=process_id,
|
|
314
|
+
exc,
|
|
315
|
+
exc_info=True,
|
|
286
316
|
)
|
|
287
|
-
|
|
317
|
+
return {"__error__": str(exc), "__step__": step_name}
|
|
288
318
|
|
|
289
319
|
|
|
290
320
|
def _run_scatter(
|
|
@@ -341,7 +371,7 @@ def finalize_scatter(
|
|
|
341
371
|
process_id: str,
|
|
342
372
|
job_id: str,
|
|
343
373
|
payload_key: str,
|
|
344
|
-
) -> dict:
|
|
374
|
+
) -> dict | None:
|
|
345
375
|
"""
|
|
346
376
|
Chord callback for ``BaseScatterProcess``.
|
|
347
377
|
|
|
@@ -350,6 +380,29 @@ def finalize_scatter(
|
|
|
350
380
|
and updates the job status.
|
|
351
381
|
"""
|
|
352
382
|
process = cast(BaseScatterProcess, get_process_registry().get_process(process_id))
|
|
383
|
+
|
|
384
|
+
errors = [r for r in step_results if isinstance(r, dict) and "__error__" in r]
|
|
385
|
+
if errors:
|
|
386
|
+
first = errors[0]
|
|
387
|
+
error_msg = (
|
|
388
|
+
f"Step '{first['__step__']}' failed: {first['__error__']}"
|
|
389
|
+
if "__step__" in first
|
|
390
|
+
else first["__error__"]
|
|
391
|
+
)
|
|
392
|
+
update_job_status(job_id, 0, error_msg, JobStatusCode.FAILED)
|
|
393
|
+
_cleanup_scatter_roster(job_id)
|
|
394
|
+
temp_result_cache.delete(payload_key)
|
|
395
|
+
logger.error(
|
|
396
|
+
"Scatter chord failed for process {} (job {}): {}/{} step(s) failed. "
|
|
397
|
+
"First error: {}",
|
|
398
|
+
process_id,
|
|
399
|
+
job_id,
|
|
400
|
+
len(errors),
|
|
401
|
+
len(step_results),
|
|
402
|
+
error_msg,
|
|
403
|
+
)
|
|
404
|
+
return None
|
|
405
|
+
|
|
353
406
|
try:
|
|
354
407
|
named_results: dict[str, dict] = {}
|
|
355
408
|
for step_name, result_ref in zip(step_names, step_results):
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
|
|
3
3
|
from fastprocesses.core.base_process import BaseProcess
|
|
4
|
+
from fastprocesses.core.config import OGCProcessesSettings
|
|
4
5
|
from fastprocesses.core.exceptions import (
|
|
5
6
|
InputValidationError,
|
|
6
7
|
ProcessClassNotFoundError,
|
|
@@ -49,17 +50,26 @@ def _run_pipeline(
|
|
|
49
50
|
Returns the (possibly modified) data dict after remote input resolution.
|
|
50
51
|
"""
|
|
51
52
|
# Step 1: validate wire-format inputs against the process description schema
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
update_job_status(
|
|
62
|
-
|
|
53
|
+
settings = OGCProcessesSettings()
|
|
54
|
+
if settings.FP_SKIP_INPUT_VALIDATION:
|
|
55
|
+
logger.warning(
|
|
56
|
+
"FP_SKIP_INPUT_VALIDATION is enabled — skipping input validation "
|
|
57
|
+
"for process {} (job {}). Do not use this in production.",
|
|
58
|
+
process_id,
|
|
59
|
+
job_id,
|
|
60
|
+
)
|
|
61
|
+
else:
|
|
62
|
+
update_job_status(
|
|
63
|
+
job_id, 0,
|
|
64
|
+
"Pre-process step 1: Validating inputs against process description.",
|
|
65
|
+
JobStatusCode.RUNNING
|
|
66
|
+
)
|
|
67
|
+
try:
|
|
68
|
+
process.validate_inputs(data["inputs"])
|
|
69
|
+
except ValueError as e:
|
|
70
|
+
logger.error(f"Input validation failed for process {process_id}: {e}")
|
|
71
|
+
update_job_status(job_id, 0, str(e), JobStatusCode.FAILED)
|
|
72
|
+
raise InputValidationError(process_id, repr(e))
|
|
63
73
|
|
|
64
74
|
# Step 2: resolve remote inputs (URI strings → downloaded data)
|
|
65
75
|
update_job_status(
|
|
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
|
{fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/core/output_schema_resolver.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastprocesses-0.22.4 → fastprocesses-0.22.6}/src/fastprocesses/processes/process_registry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|