fairagro-middleware-shared 11.0.1.dev29__tar.gz → 11.0.1.dev30__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.
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/PKG-INFO +1 -1
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/report/model.py +14 -8
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/tests/unit/test_harvest_report.py +21 -5
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/.gitignore +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/README.md +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/pyproject.toml +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/__init__.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/api_models/__init__.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/api_models/common/__init__.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/api_models/common/models.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/api_models/common/rocrate.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/api_models/py.typed +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/api_models/v1/__init__.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/api_models/v1/models.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/api_models/v2/__init__.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/api_models/v2/models.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/api_models/v3/__init__.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/api_models/v3/models.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/config/__init__.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/config/config_base.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/config/config_wrapper.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/config/logging.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/py.typed +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/report/__init__.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/report/formats/__init__.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/report/formats/base.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/report/formats/jsonld.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/src/middleware/shared/tracing.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/tests/unit/test_api_models.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/tests/unit/test_config_base.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/tests/unit/test_config_wrapper.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/tests/unit/test_logging.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/tests/unit/test_rocrate.py +0 -0
- {fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/tests/unit/test_tracing.py +0 -0
|
@@ -8,10 +8,10 @@ from datetime import UTC, datetime
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def _require_aware_utc(value: datetime, *, what: str) -> datetime:
|
|
11
|
-
"""Reject naive datetimes
|
|
11
|
+
"""Reject naive datetimes and normalize timezone-aware values to UTC."""
|
|
12
12
|
if value.tzinfo is None:
|
|
13
13
|
raise ValueError(f"{what} must be timezone-aware UTC")
|
|
14
|
-
return value
|
|
14
|
+
return value.astimezone(UTC)
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
@dataclass(frozen=True)
|
|
@@ -76,7 +76,7 @@ class RepositoryScope:
|
|
|
76
76
|
|
|
77
77
|
@property
|
|
78
78
|
def rdi(self) -> str:
|
|
79
|
-
"""
|
|
79
|
+
"""RDI identifier for this scope."""
|
|
80
80
|
return self._rdi
|
|
81
81
|
|
|
82
82
|
def close(self, *, closed_at: datetime | None = None) -> None:
|
|
@@ -183,12 +183,12 @@ class HarvestReport:
|
|
|
183
183
|
|
|
184
184
|
@property
|
|
185
185
|
def start_time(self) -> datetime:
|
|
186
|
-
"""
|
|
186
|
+
"""Run start timestamp."""
|
|
187
187
|
return self._start_time
|
|
188
188
|
|
|
189
189
|
@property
|
|
190
190
|
def end_time(self) -> datetime:
|
|
191
|
-
"""
|
|
191
|
+
"""Run end timestamp.
|
|
192
192
|
|
|
193
193
|
Raises:
|
|
194
194
|
ValueError: If :meth:`finish` has not been called yet.
|
|
@@ -199,12 +199,12 @@ class HarvestReport:
|
|
|
199
199
|
|
|
200
200
|
@property
|
|
201
201
|
def duration_seconds(self) -> float:
|
|
202
|
-
"""
|
|
202
|
+
"""Total harvest run duration in seconds."""
|
|
203
203
|
return (self.end_time - self.start_time).total_seconds()
|
|
204
204
|
|
|
205
205
|
@property
|
|
206
206
|
def repository_reports(self) -> tuple[RepositoryReport, ...]:
|
|
207
|
-
"""
|
|
207
|
+
"""Immutable snapshots of all repository scopes in open order."""
|
|
208
208
|
with self._scopes_lock:
|
|
209
209
|
scopes = list(self._scopes)
|
|
210
210
|
return tuple(scope.snapshot() for scope in scopes)
|
|
@@ -217,7 +217,13 @@ class HarvestReport:
|
|
|
217
217
|
return scope
|
|
218
218
|
|
|
219
219
|
def finish(self, *, end_time: datetime | None = None) -> None:
|
|
220
|
-
"""Finish the harvest run, close open scopes, and record the end timestamp.
|
|
220
|
+
"""Finish the harvest run, close open scopes, and record the end timestamp.
|
|
221
|
+
|
|
222
|
+
Raises:
|
|
223
|
+
ValueError: If :meth:`finish` has already been called.
|
|
224
|
+
"""
|
|
225
|
+
if self._end_time is not None:
|
|
226
|
+
raise ValueError("HarvestReport.finish() has already been called")
|
|
221
227
|
if end_time is None:
|
|
222
228
|
finished_at = datetime.now(UTC)
|
|
223
229
|
else:
|
|
@@ -8,6 +8,7 @@ import threading
|
|
|
8
8
|
from collections.abc import Callable
|
|
9
9
|
from concurrent.futures import ThreadPoolExecutor
|
|
10
10
|
from datetime import UTC, datetime, timedelta
|
|
11
|
+
from zoneinfo import ZoneInfo
|
|
11
12
|
|
|
12
13
|
import pytest
|
|
13
14
|
|
|
@@ -203,11 +204,26 @@ def test_finish_closes_open_scopes_deterministically() -> None:
|
|
|
203
204
|
scope = report.open_repository("bonares", opened_at=opened)
|
|
204
205
|
report.finish(end_time=_END)
|
|
205
206
|
assert scope.snapshot().duration_seconds == (_END - opened).total_seconds()
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
207
|
+
assert report.end_time == _END
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def test_finish_is_single_shot() -> None:
|
|
211
|
+
"""Calling finish() twice raises so run and scope durations stay aligned."""
|
|
212
|
+
report = HarvestReport(start_time=_START)
|
|
213
|
+
report.open_repository("bonares", opened_at=_START)
|
|
214
|
+
report.finish(end_time=_END)
|
|
215
|
+
with pytest.raises(ValueError, match="already been called"):
|
|
216
|
+
report.finish(end_time=_END + timedelta(seconds=30))
|
|
217
|
+
assert report.end_time == _END
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def test_require_aware_utc_normalizes_non_utc() -> None:
|
|
221
|
+
"""Timezone-aware non-UTC timestamps are stored as UTC equivalents."""
|
|
222
|
+
berlin = ZoneInfo("Europe/Berlin")
|
|
223
|
+
local_start = datetime(2026, 5, 6, 16, 0, 0, tzinfo=berlin) # UTC+2 in May
|
|
224
|
+
report = HarvestReport(start_time=local_start)
|
|
225
|
+
assert report.start_time == datetime(2026, 5, 6, 14, 0, 0, tzinfo=UTC)
|
|
226
|
+
assert report.start_time.tzinfo == UTC
|
|
211
227
|
|
|
212
228
|
|
|
213
229
|
def test_jsonld_context_and_types() -> None:
|
{fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/.gitignore
RENAMED
|
File without changes
|
{fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/README.md
RENAMED
|
File without changes
|
{fairagro_middleware_shared-11.0.1.dev29 → fairagro_middleware_shared-11.0.1.dev30}/pyproject.toml
RENAMED
|
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
|