brreg-wrapper 0.2.0__tar.gz → 0.2.3__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.
- {brreg_wrapper-0.2.0/src/brreg_wrapper.egg-info → brreg_wrapper-0.2.3}/PKG-INFO +2 -2
- {brreg_wrapper-0.2.0 → brreg_wrapper-0.2.3}/pyproject.toml +2 -2
- {brreg_wrapper-0.2.0 → brreg_wrapper-0.2.3}/src/brreg_wrapper/client.py +18 -10
- {brreg_wrapper-0.2.0 → brreg_wrapper-0.2.3}/src/brreg_wrapper/models/__init__.py +23 -0
- {brreg_wrapper-0.2.0 → brreg_wrapper-0.2.3/src/brreg_wrapper.egg-info}/PKG-INFO +2 -2
- {brreg_wrapper-0.2.0 → brreg_wrapper-0.2.3}/src/brreg_wrapper.egg-info/requires.txt +1 -1
- {brreg_wrapper-0.2.0 → brreg_wrapper-0.2.3}/tests/test_client.py +90 -4
- {brreg_wrapper-0.2.0 → brreg_wrapper-0.2.3}/LICENSE +0 -0
- {brreg_wrapper-0.2.0 → brreg_wrapper-0.2.3}/README.md +0 -0
- {brreg_wrapper-0.2.0 → brreg_wrapper-0.2.3}/setup.cfg +0 -0
- {brreg_wrapper-0.2.0 → brreg_wrapper-0.2.3}/src/brreg_wrapper/__init__.py +0 -0
- {brreg_wrapper-0.2.0 → brreg_wrapper-0.2.3}/src/brreg_wrapper/exceptions.py +0 -0
- {brreg_wrapper-0.2.0 → brreg_wrapper-0.2.3}/src/brreg_wrapper.egg-info/SOURCES.txt +0 -0
- {brreg_wrapper-0.2.0 → brreg_wrapper-0.2.3}/src/brreg_wrapper.egg-info/dependency_links.txt +0 -0
- {brreg_wrapper-0.2.0 → brreg_wrapper-0.2.3}/src/brreg_wrapper.egg-info/top_level.txt +0 -0
- {brreg_wrapper-0.2.0 → brreg_wrapper-0.2.3}/tests/test_exceptions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: brreg-wrapper
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: A Python wrapper for the Brønnøysund Register Centre (Brreg) API.
|
|
5
5
|
Author-email: Sondre Dyrnes <sondre@villdyr.no>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -18,7 +18,7 @@ Description-Content-Type: text/markdown
|
|
|
18
18
|
License-File: LICENSE
|
|
19
19
|
Requires-Dist: httpx<1.0,>=0.27
|
|
20
20
|
Requires-Dist: pydantic<3.0,>=2.0
|
|
21
|
-
Requires-Dist: tenacity<
|
|
21
|
+
Requires-Dist: tenacity<10.0,>=8.2
|
|
22
22
|
Provides-Extra: dev
|
|
23
23
|
Requires-Dist: pytest<9.0,>=8.0; extra == "dev"
|
|
24
24
|
Requires-Dist: pytest-asyncio<1.0,>=0.23; extra == "dev"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "brreg-wrapper"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.3"
|
|
4
4
|
description = "A Python wrapper for the Brønnøysund Register Centre (Brreg) API."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
@@ -21,7 +21,7 @@ classifiers = [
|
|
|
21
21
|
dependencies = [
|
|
22
22
|
"httpx>=0.27,<1.0",
|
|
23
23
|
"pydantic>=2.0,<3.0",
|
|
24
|
-
"tenacity>=8.2,<
|
|
24
|
+
"tenacity>=8.2,<10.0",
|
|
25
25
|
]
|
|
26
26
|
|
|
27
27
|
[project.urls]
|
|
@@ -196,6 +196,22 @@ class BrregClient:
|
|
|
196
196
|
request_params=request_params,
|
|
197
197
|
)
|
|
198
198
|
|
|
199
|
+
def _log_mapped_http_error(self, exc: httpx.HTTPStatusError) -> None:
|
|
200
|
+
"""Log a mapped HTTP error at a level matching its severity.
|
|
201
|
+
|
|
202
|
+
404 is an expected miss (callers often try enhet, then underenhet),
|
|
203
|
+
so it is logged at DEBUG without exception info. Other HTTP errors
|
|
204
|
+
stay at ERROR with traceback.
|
|
205
|
+
"""
|
|
206
|
+
message = (
|
|
207
|
+
f"HTTP error {exc.response.status_code} for {exc.request.url}: "
|
|
208
|
+
f"{exc.response.text}"
|
|
209
|
+
)
|
|
210
|
+
if exc.response.status_code == 404:
|
|
211
|
+
self._logger.debug(message)
|
|
212
|
+
else:
|
|
213
|
+
self._logger.error(message, exc_info=True)
|
|
214
|
+
|
|
199
215
|
async def _request(
|
|
200
216
|
self,
|
|
201
217
|
method: str,
|
|
@@ -251,11 +267,7 @@ class BrregClient:
|
|
|
251
267
|
return response
|
|
252
268
|
except httpx.HTTPStatusError as exc:
|
|
253
269
|
error = self._map_http_error(exc)
|
|
254
|
-
self.
|
|
255
|
-
f"HTTP error {exc.response.status_code} for {exc.request.url}: "
|
|
256
|
-
f"{exc.response.text}",
|
|
257
|
-
exc_info=True,
|
|
258
|
-
)
|
|
270
|
+
self._log_mapped_http_error(exc)
|
|
259
271
|
raise error
|
|
260
272
|
except httpx.TimeoutException as exc:
|
|
261
273
|
self._logger.error(f"Request timed out: {exc}", exc_info=True)
|
|
@@ -332,11 +344,7 @@ class BrregClient:
|
|
|
332
344
|
return response
|
|
333
345
|
except httpx.HTTPStatusError as exc:
|
|
334
346
|
error = self._map_http_error(exc)
|
|
335
|
-
self.
|
|
336
|
-
f"HTTP error {exc.response.status_code} for {exc.request.url}: "
|
|
337
|
-
f"{exc.response.text}",
|
|
338
|
-
exc_info=True,
|
|
339
|
-
)
|
|
347
|
+
self._log_mapped_http_error(exc)
|
|
340
348
|
raise error
|
|
341
349
|
except httpx.TimeoutException as exc:
|
|
342
350
|
self._logger.error(f"Request timed out: {exc}", exc_info=True)
|
|
@@ -162,6 +162,23 @@ class LegacyPaategning(BaseModel):
|
|
|
162
162
|
)
|
|
163
163
|
|
|
164
164
|
|
|
165
|
+
class HistoriskNavn(BaseModel):
|
|
166
|
+
model_config = ConfigDict(
|
|
167
|
+
extra="forbid",
|
|
168
|
+
)
|
|
169
|
+
navn: str = Field(..., description="Historisk navn")
|
|
170
|
+
fraDato: str = Field(
|
|
171
|
+
...,
|
|
172
|
+
description="Dato navnet ble gyldig fra",
|
|
173
|
+
examples=["1995-02-22 22:56:00"],
|
|
174
|
+
)
|
|
175
|
+
tilDato: str = Field(
|
|
176
|
+
...,
|
|
177
|
+
description="Dato navnet var gyldig til",
|
|
178
|
+
examples=["1996-05-22 19:00:08"],
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
165
182
|
class Self1(BaseModel):
|
|
166
183
|
model_config = ConfigDict(
|
|
167
184
|
extra="forbid",
|
|
@@ -572,6 +589,9 @@ class Enhet(BaseModel):
|
|
|
572
589
|
mobil: Optional[str] = Field(
|
|
573
590
|
None, description="Enhetens mobilnummer", examples=["91504800"]
|
|
574
591
|
)
|
|
592
|
+
historiskeNavn: Optional[Sequence[HistoriskNavn]] = Field(
|
|
593
|
+
None, description="Enhetens historiske navn"
|
|
594
|
+
)
|
|
575
595
|
field_links: Optional[FieldLinks2] = Field(None, alias="_links")
|
|
576
596
|
|
|
577
597
|
|
|
@@ -1243,6 +1263,9 @@ class Underenhet(BaseModel):
|
|
|
1243
1263
|
mobil: Optional[str] = Field(
|
|
1244
1264
|
None, description="Underenhetens mobilnummer", examples=["91504800"]
|
|
1245
1265
|
)
|
|
1266
|
+
historiskeNavn: Optional[Sequence[HistoriskNavn]] = Field(
|
|
1267
|
+
None, description="Underenhetens historiske navn"
|
|
1268
|
+
)
|
|
1246
1269
|
field_links: Optional[FieldLinks13] = Field(None, alias="_links")
|
|
1247
1270
|
|
|
1248
1271
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: brreg-wrapper
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: A Python wrapper for the Brønnøysund Register Centre (Brreg) API.
|
|
5
5
|
Author-email: Sondre Dyrnes <sondre@villdyr.no>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -18,7 +18,7 @@ Description-Content-Type: text/markdown
|
|
|
18
18
|
License-File: LICENSE
|
|
19
19
|
Requires-Dist: httpx<1.0,>=0.27
|
|
20
20
|
Requires-Dist: pydantic<3.0,>=2.0
|
|
21
|
-
Requires-Dist: tenacity<
|
|
21
|
+
Requires-Dist: tenacity<10.0,>=8.2
|
|
22
22
|
Provides-Extra: dev
|
|
23
23
|
Requires-Dist: pytest<9.0,>=8.0; extra == "dev"
|
|
24
24
|
Requires-Dist: pytest-asyncio<1.0,>=0.23; extra == "dev"
|
|
@@ -24,6 +24,7 @@ from brreg_wrapper.models import (
|
|
|
24
24
|
Kommuner1, # Added import
|
|
25
25
|
Page,
|
|
26
26
|
SlettetEnhet,
|
|
27
|
+
Underenhet,
|
|
27
28
|
)
|
|
28
29
|
|
|
29
30
|
|
|
@@ -289,7 +290,9 @@ async def test_async_context_manager():
|
|
|
289
290
|
|
|
290
291
|
|
|
291
292
|
@pytest.mark.asyncio
|
|
292
|
-
async def test_get_enhet_not_found(
|
|
293
|
+
async def test_get_enhet_not_found(
|
|
294
|
+
httpx_mock: HTTPXMock, caplog: pytest.LogCaptureFixture
|
|
295
|
+
):
|
|
293
296
|
"""Test handling of a 404 Not Found error when retrieving an entity."""
|
|
294
297
|
org_nr = "123456789"
|
|
295
298
|
expected_url = f"{BrregClient.BASE_URL}/enheter/{org_nr}"
|
|
@@ -303,10 +306,16 @@ async def test_get_enhet_not_found(httpx_mock: HTTPXMock):
|
|
|
303
306
|
|
|
304
307
|
client = BrregClient()
|
|
305
308
|
try:
|
|
306
|
-
with
|
|
307
|
-
|
|
309
|
+
with caplog.at_level("DEBUG", logger="brreg_wrapper.client"):
|
|
310
|
+
with pytest.raises(BrregResourceNotFoundError) as exc_info:
|
|
311
|
+
await client.get_enhet(org_nr)
|
|
308
312
|
|
|
309
|
-
assert exc_info.value.
|
|
313
|
+
assert exc_info.value.status_code == 404
|
|
314
|
+
assert not any(record.levelname == "ERROR" for record in caplog.records)
|
|
315
|
+
assert any(
|
|
316
|
+
record.levelname == "DEBUG" and "HTTP error 404" in record.message
|
|
317
|
+
for record in caplog.records
|
|
318
|
+
)
|
|
310
319
|
|
|
311
320
|
# Verify the request was made
|
|
312
321
|
request = httpx_mock.get_request()
|
|
@@ -316,6 +325,36 @@ async def test_get_enhet_not_found(httpx_mock: HTTPXMock):
|
|
|
316
325
|
await client.close()
|
|
317
326
|
|
|
318
327
|
|
|
328
|
+
@pytest.mark.asyncio
|
|
329
|
+
async def test_get_enhet_server_error_logs_at_error(
|
|
330
|
+
httpx_mock: HTTPXMock, caplog: pytest.LogCaptureFixture
|
|
331
|
+
):
|
|
332
|
+
"""5xx responses should still be logged at ERROR with exception info."""
|
|
333
|
+
org_nr = "123456789"
|
|
334
|
+
expected_url = f"{BrregClient.BASE_URL}/enheter/{org_nr}"
|
|
335
|
+
|
|
336
|
+
httpx_mock.add_response(
|
|
337
|
+
url=expected_url,
|
|
338
|
+
method="GET",
|
|
339
|
+
status_code=500,
|
|
340
|
+
json={"message": "Internal Server Error"},
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
client = BrregClient(max_retries=0)
|
|
344
|
+
try:
|
|
345
|
+
with caplog.at_level("DEBUG", logger="brreg_wrapper.client"):
|
|
346
|
+
with pytest.raises(BrregServerError) as exc_info:
|
|
347
|
+
await client.get_enhet(org_nr)
|
|
348
|
+
|
|
349
|
+
assert exc_info.value.status_code == 500
|
|
350
|
+
error_records = [r for r in caplog.records if r.levelname == "ERROR"]
|
|
351
|
+
assert error_records
|
|
352
|
+
assert any("HTTP error 500" in record.message for record in error_records)
|
|
353
|
+
assert any(record.exc_info is not None for record in error_records)
|
|
354
|
+
finally:
|
|
355
|
+
await client.close()
|
|
356
|
+
|
|
357
|
+
|
|
319
358
|
@pytest.mark.asyncio
|
|
320
359
|
async def test_get_enhet_deleted(httpx_mock: HTTPXMock):
|
|
321
360
|
"""Test retrieving a deleted entity."""
|
|
@@ -683,3 +722,50 @@ async def test_response_json_property():
|
|
|
683
722
|
# Test with no response text
|
|
684
723
|
error = BrregAPIError(message="Test error", status_code=400)
|
|
685
724
|
assert error.response_json is None
|
|
725
|
+
|
|
726
|
+
|
|
727
|
+
@pytest.mark.asyncio
|
|
728
|
+
async def test_get_underenhet_with_historiske_navn(httpx_mock: HTTPXMock):
|
|
729
|
+
"""Underenhet responses may include historiskeNavn from the live API."""
|
|
730
|
+
org_nr = "971917407"
|
|
731
|
+
mock_response_data = {
|
|
732
|
+
"organisasjonsnummer": org_nr,
|
|
733
|
+
"navn": "BETONMAST INNLANDET AS",
|
|
734
|
+
"organisasjonsform": {
|
|
735
|
+
"kode": "BEDR",
|
|
736
|
+
"beskrivelse": "Underenhet til næringsdrivende og offentlig forvaltning",
|
|
737
|
+
"_links": {
|
|
738
|
+
"self": {"href": f"{BrregClient.BASE_URL}/organisasjonsformer/BEDR"}
|
|
739
|
+
},
|
|
740
|
+
},
|
|
741
|
+
"historiskeNavn": [
|
|
742
|
+
{
|
|
743
|
+
"navn": "TOTEN BYGG OG ANLEGG AS",
|
|
744
|
+
"fraDato": "1996-05-22 19:00:08",
|
|
745
|
+
"tilDato": "2003-01-27 20:36:38",
|
|
746
|
+
}
|
|
747
|
+
],
|
|
748
|
+
"registrertIMvaregisteret": False,
|
|
749
|
+
"registreringsdatoEnhetsregisteret": "1995-02-22",
|
|
750
|
+
"harRegistrertAntallAnsatte": True,
|
|
751
|
+
"_links": {"self": {"href": f"{BrregClient.BASE_URL}/underenheter/{org_nr}"}},
|
|
752
|
+
}
|
|
753
|
+
expected_url = f"{BrregClient.BASE_URL}/underenheter/{org_nr}"
|
|
754
|
+
|
|
755
|
+
httpx_mock.add_response(
|
|
756
|
+
url=expected_url,
|
|
757
|
+
method="GET",
|
|
758
|
+
json=mock_response_data,
|
|
759
|
+
status_code=200,
|
|
760
|
+
headers={"Content-Type": "application/json"},
|
|
761
|
+
)
|
|
762
|
+
|
|
763
|
+
client = BrregClient()
|
|
764
|
+
try:
|
|
765
|
+
underenhet = await client.get_underenhet(org_nr)
|
|
766
|
+
assert isinstance(underenhet, Underenhet)
|
|
767
|
+
assert underenhet.navn == "BETONMAST INNLANDET AS"
|
|
768
|
+
assert underenhet.historiskeNavn is not None
|
|
769
|
+
assert underenhet.historiskeNavn[0].navn == "TOTEN BYGG OG ANLEGG AS"
|
|
770
|
+
finally:
|
|
771
|
+
await client.close()
|
|
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
|