lodapi 0.0.1__tar.gz → 0.0.2__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.
- lodapi-0.0.2/CHANGELOG.md +26 -0
- {lodapi-0.0.1 → lodapi-0.0.2}/PKG-INFO +1 -1
- {lodapi-0.0.1 → lodapi-0.0.2}/pyproject.toml +7 -1
- {lodapi-0.0.1 → lodapi-0.0.2}/src/lodapi/client.py +1 -1
- {lodapi-0.0.1 → lodapi-0.0.2}/src/lodapi/models.py +20 -5
- {lodapi-0.0.1 → lodapi-0.0.2}/tests/conftest.py +11 -1
- {lodapi-0.0.1 → lodapi-0.0.2}/tests/test_buildings.py +4 -1
- lodapi-0.0.2/tests/test_live_smoke.py +47 -0
- {lodapi-0.0.1 → lodapi-0.0.2}/.gitignore +0 -0
- {lodapi-0.0.1 → lodapi-0.0.2}/README.md +0 -0
- {lodapi-0.0.1 → lodapi-0.0.2}/src/lodapi/__init__.py +0 -0
- {lodapi-0.0.1 → lodapi-0.0.2}/src/lodapi/_geo.py +0 -0
- {lodapi-0.0.1 → lodapi-0.0.2}/src/lodapi/_http.py +0 -0
- {lodapi-0.0.1 → lodapi-0.0.2}/src/lodapi/exceptions.py +0 -0
- {lodapi-0.0.1 → lodapi-0.0.2}/src/lodapi/resources/__init__.py +0 -0
- {lodapi-0.0.1 → lodapi-0.0.2}/src/lodapi/resources/buildings.py +0 -0
- {lodapi-0.0.1 → lodapi-0.0.2}/src/lodapi/resources/terrain.py +0 -0
- {lodapi-0.0.1 → lodapi-0.0.2}/src/lodapi/resources/tilesets.py +0 -0
- {lodapi-0.0.1 → lodapi-0.0.2}/tests/__init__.py +0 -0
- {lodapi-0.0.1 → lodapi-0.0.2}/tests/test_geo_extra.py +0 -0
- {lodapi-0.0.1 → lodapi-0.0.2}/tests/test_glb.py +0 -0
- {lodapi-0.0.1 → lodapi-0.0.2}/tests/test_terrain_tilesets_datasets.py +0 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.0.2 — 2026-06-14
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **`buildings()` crashte bei jedem Call** durch Schema-Drift: das Modell
|
|
7
|
+
`BuildingsBboxAttribution` verlangte ein Pflichtfeld `bundesland`, die Live-API
|
|
8
|
+
liefert aber `bundesland_code` (+ `license_id`, `attribution_url`, `license_url`,
|
|
9
|
+
`requires_attribution`). Modell an die **echte** Live-Form angepasst
|
|
10
|
+
(verifiziert gegen api.lodapi.de). Ursache: der committete OpenAPI-Snapshot,
|
|
11
|
+
aus dem die v0-Modelle abgeleitet wurden, war gegenüber dem Prod-Deploy stale.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- Response-Modelle der buildings-Endpoints (`BuildingsBboxAttribution`,
|
|
15
|
+
`BuildingDetail`) auf **durchgängig Optional** umgestellt. Ein Client-SDK gegen
|
|
16
|
+
eine junge API darf nicht an einem umbenannten/fehlenden Feld crashen
|
|
17
|
+
(`extra="allow"` fängt *neue* Felder, Optional fängt *entfernte/umbenannte*).
|
|
18
|
+
- Neuer opt-in **Live-Smoke-Test** (`pytest -m live`) gegen api.lodapi.de, der
|
|
19
|
+
genau diese Art Drift künftig fängt — die gemockten Unit-Tests prüften das
|
|
20
|
+
Fixture, nicht die reale API.
|
|
21
|
+
|
|
22
|
+
## 0.0.1 — 2026-06-14
|
|
23
|
+
|
|
24
|
+
- Erstes Release: ergonomische Facade über der Lodapi-REST-API
|
|
25
|
+
(buildings/terrain/tilesets/datasets), `X-API-Key`-Auth, Free-Tier,
|
|
26
|
+
optionales `[geo]`-Extra, RFC-7807-Fehler, Soft-Quota-Header.
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "lodapi"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.2"
|
|
8
8
|
description = "Ergonomic Python SDK for the Lodapi REST API (LoD2 buildings, terrain, 3D tiles)."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -46,3 +46,9 @@ packages = ["src/lodapi"]
|
|
|
46
46
|
|
|
47
47
|
[tool.pytest.ini_options]
|
|
48
48
|
testpaths = ["tests"]
|
|
49
|
+
# Live-Tests (echte api.lodapi.de) standardmäßig ausgeschlossen — CI/offline
|
|
50
|
+
# läuft rein gegen respx-Mocks. Explizit anfordern via: pytest -m live
|
|
51
|
+
addopts = "-m 'not live'"
|
|
52
|
+
markers = [
|
|
53
|
+
"live: hits the real api.lodapi.de (network); run with `pytest -m live`",
|
|
54
|
+
]
|
|
@@ -47,9 +47,22 @@ class _Model(BaseModel):
|
|
|
47
47
|
# --------------------------------------------------------------------------
|
|
48
48
|
|
|
49
49
|
class BuildingsBboxAttribution(_Model):
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
"""Per-Bundesland attribution/licence block in ``lodapi.attribution[]``.
|
|
51
|
+
|
|
52
|
+
Shape verified against the live API (api.lodapi.de, 2026-06-14). All fields
|
|
53
|
+
are Optional on purpose: this is a young, evolving API and a client SDK must
|
|
54
|
+
never crash on a renamed/missing attribution field (``extra="allow"`` covers
|
|
55
|
+
*added* fields; Optional covers *removed/renamed* ones). The committed
|
|
56
|
+
OpenAPI snapshot lagged here (had ``bundesland``/``license``) — see SDK
|
|
57
|
+
CHANGELOG 0.0.2.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
bundesland_code: Optional[str] = None
|
|
61
|
+
license_id: Optional[str] = None
|
|
52
62
|
attribution_text: Optional[str] = None
|
|
63
|
+
attribution_url: Optional[str] = None
|
|
64
|
+
license_url: Optional[str] = None
|
|
65
|
+
requires_attribution: Optional[bool] = None
|
|
53
66
|
|
|
54
67
|
|
|
55
68
|
class BuildingsBboxLodapiMeta(_Model):
|
|
@@ -90,9 +103,11 @@ class BuildingsBboxResponse(_Model):
|
|
|
90
103
|
# --------------------------------------------------------------------------
|
|
91
104
|
|
|
92
105
|
class BuildingDetail(_Model):
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
106
|
+
# All Optional by design — a client SDK must not crash if the API renames
|
|
107
|
+
# or drops a field (extra="allow" covers added fields). See 0.0.2 fix.
|
|
108
|
+
gmlid: Optional[str] = None
|
|
109
|
+
bundesland_code: Optional[str] = None
|
|
110
|
+
building_id: Optional[int] = None
|
|
96
111
|
geometry: Optional[dict[str, Any]] = None
|
|
97
112
|
lod: str = "LoD2"
|
|
98
113
|
|
|
@@ -44,8 +44,18 @@ def buildings_page(next_cursor=None, ids=("A", "B")):
|
|
|
44
44
|
],
|
|
45
45
|
"lodapi": {
|
|
46
46
|
"next": next_cursor,
|
|
47
|
+
# Real live-API shape (verified api.lodapi.de 2026-06-14): the
|
|
48
|
+
# attribution block uses bundesland_code/license_id + url/flag fields,
|
|
49
|
+
# NOT the stale bundesland/license the snapshot once had.
|
|
47
50
|
"attribution": [
|
|
48
|
-
{
|
|
51
|
+
{
|
|
52
|
+
"bundesland_code": "bw",
|
|
53
|
+
"license_id": "dl-de-zero-2.0",
|
|
54
|
+
"attribution_text": "© LGL-BW",
|
|
55
|
+
"attribution_url": "https://www.lgl-bw.de/",
|
|
56
|
+
"license_url": "https://www.govdata.de/dl-de/zero-2-0",
|
|
57
|
+
"requires_attribution": False,
|
|
58
|
+
}
|
|
49
59
|
],
|
|
50
60
|
},
|
|
51
61
|
}
|
|
@@ -37,7 +37,10 @@ def test_buildings_single_page(client):
|
|
|
37
37
|
|
|
38
38
|
assert page.count == 2
|
|
39
39
|
assert len(page.features) == 2
|
|
40
|
-
|
|
40
|
+
att = page.lodapi.attribution[0]
|
|
41
|
+
assert att.bundesland_code == "bw"
|
|
42
|
+
assert att.license_id == "dl-de-zero-2.0"
|
|
43
|
+
assert att.requires_attribution is False
|
|
41
44
|
# bbox tuple serialized into the query, limit forwarded, no `bl` param
|
|
42
45
|
req = route.calls.last.request
|
|
43
46
|
assert req.url.params["bbox"] == "7.0,50.9,7.1,51.0"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Opt-in live smoke tests against the real api.lodapi.de.
|
|
2
|
+
|
|
3
|
+
These are EXCLUDED from the default run (`addopts = -m 'not live'` in
|
|
4
|
+
pyproject) because CI runs offline against respx mocks. Run explicitly with::
|
|
5
|
+
|
|
6
|
+
uv run --extra dev pytest -m live
|
|
7
|
+
|
|
8
|
+
Why these exist: the 0.0.2 buildings() crash slipped through because the
|
|
9
|
+
mocked unit tests validated the *fixture*, not the live API. A real call
|
|
10
|
+
parsing through the Pydantic models is the only thing that catches schema
|
|
11
|
+
drift between the SDK and the deployed API.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import pytest
|
|
17
|
+
|
|
18
|
+
from lodapi import Client
|
|
19
|
+
from lodapi.models import BuildingsBboxResponse
|
|
20
|
+
|
|
21
|
+
# Small bbox over Cologne (NRW) — stable, non-empty, free-tier (no key needed).
|
|
22
|
+
_BBOX = (7.00, 50.94, 7.02, 50.95)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.mark.live
|
|
26
|
+
def test_buildings_bbox_parses_against_live_api() -> None:
|
|
27
|
+
"""buildings() must parse a real federated response without raising."""
|
|
28
|
+
with Client() as c:
|
|
29
|
+
page = c.buildings(bbox=_BBOX, limit=1)
|
|
30
|
+
|
|
31
|
+
assert isinstance(page, BuildingsBboxResponse)
|
|
32
|
+
assert page.type == "FeatureCollection"
|
|
33
|
+
assert isinstance(page.count, int)
|
|
34
|
+
# The attribution block is the field that drifted in 0.0.2 — assert it
|
|
35
|
+
# parses and carries the real live shape.
|
|
36
|
+
if page.lodapi.attribution:
|
|
37
|
+
att = page.lodapi.attribution[0]
|
|
38
|
+
assert att.bundesland_code is not None
|
|
39
|
+
# license/url/flag are tolerated as Optional, just must not raise.
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@pytest.mark.live
|
|
43
|
+
def test_datasets_parses_against_live_api() -> None:
|
|
44
|
+
with Client() as c:
|
|
45
|
+
ds = c.datasets()
|
|
46
|
+
assert ds.datasets, "expected at least one live dataset"
|
|
47
|
+
assert ds.datasets[0].bundesland_code
|
|
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
|