Skip to content
This repository was archived by the owner on Nov 14, 2025. It is now read-only.

Commit 49ff9e3

Browse files
committed
Refactored Entiti constructor with multipledispatch
1 parent aa81d83 commit 49ff9e3

10 files changed

Lines changed: 62 additions & 109 deletions

File tree

poetry.lock

Lines changed: 13 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "ngsildclient"
3-
version = "0.5.0"
3+
version = "0.5.1"
44
description = "A Python library that helps building NGSI-LD entities and interacting with a NGSI-LD Context Broker"
55
license = "Apache-2.0"
66
readme="README.md"
@@ -22,6 +22,7 @@ isodate = "^0.6.1"
2222
networkx = "^2.8.7"
2323
scalpl = "^0.4.2"
2424
python-dateutil = "^2.8.2"
25+
multipledispatch = "^0.6.0"
2526

2627
[tool.poetry.dev-dependencies]
2728
pytest = "^7.1.3"

src/ngsildclient/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import logging
1414
import sys
1515

16-
__version__ = "0.5.0"
16+
__version__ = "0.5.1"
1717

1818
from ngsildclient.settings import globalsettings as settings
1919
from .utils import iso8601, is_interactive

src/ngsildclient/model/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
Sequence[entity.Entity],
4646
]
4747

48+
LD_PREFIX = "urn:ngsi-ld"
49+
4850
NgsiLocation = Union[tuple[int, int], Point]
4951
"""A user type : either a tuple of two ints (lat, lon) or a GeoJson Point.
5052
"""

src/ngsildclient/model/entity.py

Lines changed: 33 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
from copy import deepcopy
2121
from functools import partialmethod
22-
2322
from datetime import datetime
2423
from typing import (
2524
Sequence,
@@ -31,13 +30,13 @@
3130
Mapping,
3231
Callable,
3332
)
34-
33+
from multipledispatch import dispatch
3534

3635
from ngsildclient.model.ngsidict import NgsiDict
3736
from ngsildclient.utils import iso8601, url
3837
from ngsildclient.utils.urn import Urn
3938
from ngsildclient.model.exceptions import NgsiMissingIdError, NgsiMissingTypeError, NgsiMissingContextError
40-
from ngsildclient.model.constants import CORE_CONTEXT, Rel, NgsiDate, NgsiGeometry
39+
from ngsildclient.model.constants import CORE_CONTEXT, LD_PREFIX, Rel, NgsiDate, NgsiGeometry
4140
from ngsildclient.settings import globalsettings
4241

4342
logger = logging.getLogger(__name__)
@@ -185,96 +184,38 @@ class Entity:
185184
>>> e.rm("NO2.accuracy")
186185
"""
187186

188-
def __init__(
189-
self,
190-
*args: str,
191-
ctx: List = None,
192-
payload: dict = None,
193-
autoprefix: Optional[bool] = None,
194-
):
195-
"""Create a NGSI-LD compliant entity
196-
197-
Expected args are : the Entity's type and identifier.
198-
Example : Entity("AirQualityObserved", "RZ:Obsv4567")
199-
The fully qualified identifier is built following the naming convention : "urn:ngsi-ld:{type}:{id}'.
200-
"urn:ngsi-ld:" is added if not specified.
201-
202-
Alernatively a single arg is allowed : the fully qualified Entity's identifier.
203-
Example : Entity("urn:ngsi-ld:AirQualityObserved:RZ:Obsv4567")
204-
Type is inferred from the fully qualified identifier.
205-
206-
The default behaviour can be disabled : Entity.globalsettings.autoprefix = False.
207-
208-
209-
Parameters
210-
----------
211-
type : str
212-
entity type
213-
id : str
214-
entity identifier
215-
ctx : list, optional
216-
the NGSI-LD context, by default the NGSI-LD Core Context
217-
218-
Example
219-
-------
220-
>>> from ngsildclient.model.entity import Entity
221-
>>> e1 = Entity("AirQualityObserved", "urn:ngsi-ld:AirQualityObserved:RZ:Obsv4567") # long form
222-
>>> e2 = Entity("AirQualityObserved", "AirQualityObserved:RZ:Obsv4567") # omit scheme + nss
223-
>>> e3 = Entity("AirQualityObserved", "RZ:Obsv4567") # omit scheme + nss + type
224-
>>> print(e1 == e2 == e3)
225-
True
226-
>>> e1.pprint()
227-
{
228-
"@context": [
229-
"https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld"
230-
],
231-
"id": "urn:ngsi-ld:AirQualityObserved:RZ:Obsv4567",
232-
"type": "AirQualityObserved"
233-
}
234-
"""
235-
self.root: NgsiDict = None
236-
self._lastprop: NgsiDict = None
187+
@staticmethod
188+
def _build_fully_qualified_id(type: str, id: str) -> Urn:
189+
if globalsettings.autoprefix:
190+
bare_id = Urn.unprefix(id)
191+
if not bare_id.startswith(f"{type}:"):
192+
id = f"{type}:{bare_id}"
193+
return Urn.prefix(id)
194+
195+
@dispatch(dict)
196+
def __init__(self, payload: dict):
197+
if not payload.get("id", None):
198+
raise NgsiMissingIdError()
199+
if not payload.get("type", None):
200+
raise NgsiMissingTypeError()
201+
if not payload.get("@context"):
202+
payload["@context"] = [CORE_CONTEXT]
203+
self._lastprop = self.root = NgsiDict(payload)
237204
self._anchored: bool = False
238205
self._lastwasmulti: bool = False
239206

240-
if len(args) == 2:
241-
type, id = args
242-
elif len(args) == 1:
243-
id, type = args[0], None
244-
elif len(args) == 0 and payload is not None: # create Entity from a dictionary
245-
if not payload.get("id", None):
246-
raise NgsiMissingIdError()
247-
if not payload.get("type", None):
248-
raise NgsiMissingTypeError()
249-
if not payload.get("@context", None):
250-
raise NgsiMissingContextError()
251-
self._lastprop = self.root = NgsiDict(payload)
252-
return
253-
else:
254-
raise ValueError("Expecteds args : type, id (alt. fully qualified id)")
255-
256-
if autoprefix is None:
257-
autoprefix = globalsettings.autoprefix
258-
if not ctx:
259-
ctx = [CORE_CONTEXT]
260-
261-
# create a new Entity using its id and type
262-
if type is None: # try to infer type from the fully qualified identifier
263-
id = Urn.prefix(id)
264-
urn = Urn(id)
265-
if (type := urn.infertype()) is None:
266-
raise NgsiMissingTypeError(f"{urn.fqn=}")
267-
else: # type is not None
268-
autoprefix &= not Urn.is_prefixed(id)
269-
if autoprefix:
270-
bareid = Urn.shorten(id)
271-
prefix = f"{type}:"
272-
if not bareid.startswith(prefix):
273-
id = prefix + bareid
274-
id = Urn.prefix(id) # set the prefix "urn:ngsi-ld:" if not already done
275-
urn = Urn(id)
276-
277-
self._lastprop = self.root = NgsiDict({"@context": ctx, "id": urn.fqn, "type": type})
207+
@dispatch(str, str)
208+
def __init__(self, type: str, id: str, *, ctx: List[str] = None): # noqa F811
209+
id = Entity._build_fully_qualified_id(type, id)
210+
self.__init__({"id": id, "type": type, "@context": ctx or [CORE_CONTEXT]})
211+
212+
@dispatch(str)
213+
def __init__(self, id: str, *, ctx: List[str] = None): # noqa F811
214+
id = Urn.prefix(id)
215+
urn = Urn(id)
216+
if (type := urn.infertype()) is None:
217+
raise NgsiMissingTypeError(f"{urn.fqn=}")
218+
self.__init__({"id": id, "type": type, "@context": ctx or [CORE_CONTEXT]})
278219

279220
@classmethod
280221
def from_dict(cls, payload: dict):
@@ -293,7 +234,7 @@ def from_dict(cls, payload: dict):
293234
Entity
294235
The result Entity instance
295236
"""
296-
return cls(payload=payload)
237+
return cls(payload)
297238

298239
@classmethod
299240
def from_json(cls, content: str):
@@ -313,7 +254,7 @@ def from_json(cls, content: str):
313254
The result Entity instance
314255
"""
315256
payload: dict = json.loads(content)
316-
return cls(payload=payload)
257+
return cls(payload)
317258

318259
@classmethod
319260
def duplicate(cls, entity: Entity) -> Entity:

src/ngsildclient/utils/urn.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import logging
2323

2424
from typing import overload, Optional, Tuple
25-
from ngsildclient.api.constants import ENDPOINT_ENTITIES
2625

2726
NID_PATTERN = re.compile(r"^[0-9a-zA-Z\-]+$")
2827
"""Regex pattern that matches a valid namespace identifier (`regex.Pattern`).
@@ -49,8 +48,6 @@ class Urn:
4948
"""Helper class to handle NGSI-LD urn/uri and allow to work with unprefixed strings."""
5049

5150
DEFAULT_NID = "ngsi-ld"
52-
# """Default NGSI-LD namespace value
53-
# """
5451

5552
@overload
5653
def __init__(self, fqn: str) -> None:
@@ -202,7 +199,7 @@ def prefix(value: str) -> str:
202199
return value if Urn.is_prefixed(value) else f"urn:ngsi-ld:{value}"
203200

204201
@staticmethod
205-
def shorten(value: str) -> str:
202+
def unprefix(value: str) -> str:
206203
"""Remove the prefix (URN scheme+NID)
207204
208205
Parameters

tests/test_entity.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,14 @@ def test_constructor_type_and_id_without_scheme():
6666
assert e.id == "urn:ngsi-ld:AirQualityObserved:RZ:Obsv4567"
6767

6868

69-
def test_constructor_autoprefix_type_and_id_without_type_included():
70-
e = Entity("AirQualityObserved", "RZ:Obsv4567", autoprefix=True)
69+
def test_constructor_type_and_id_without_type_included():
70+
e = Entity("AirQualityObserved", "RZ:Obsv4567")
7171
assert e.type == "AirQualityObserved"
7272
assert e.id == "urn:ngsi-ld:AirQualityObserved:RZ:Obsv4567"
7373

7474

75-
def test_constructor_autoprefix_type_and_id_with_type_included():
76-
e = Entity("AirQualityObserved", "AirQualityObserved:RZ:Obsv4567", autoprefix=True)
75+
def test_constructor_type_and_id_with_type_included():
76+
e = Entity("AirQualityObserved", "AirQualityObserved:RZ:Obsv4567")
7777
assert e.type == "AirQualityObserved"
7878
assert e.id == "urn:ngsi-ld:AirQualityObserved:RZ:Obsv4567"
7979

@@ -140,7 +140,7 @@ def test_air_quality_from_dict(expected_air_quality):
140140
"object": "urn:ngsi-ld:PointOfInterest:RZ:MainSquare",
141141
},
142142
}
143-
e = Entity.from_dict(payload=payload)
143+
e = Entity.from_dict(payload)
144144
assert e.to_dict() == expected_air_quality
145145

146146

@@ -207,7 +207,7 @@ def test_store():
207207
"""
208208
e = Entity(
209209
"Store",
210-
"Store:001",
210+
"001",
211211
ctx=[
212212
{
213213
"Store": "https://uri.etsi.org/ngsi-ld/primer/Store",

tests/test_ngsidict.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def test_type():
2626
"type": "Barn",
2727
"fillingLevel": {"type": "Property", "value": 0.6},
2828
}
29-
e = Entity(payload=d)
29+
e = Entity(d)
3030
prop = e["fillingLevel"]
3131
assert prop.type == "Property"
3232

tests/test_ngsildclient.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@
1313

1414

1515
def test_version():
16-
assert __version__ == "0.5.0"
16+
assert __version__ == "0.5.1"

tests/test_urn.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,12 @@ def test_valid_urn():
4545

4646
def test_invalid_urn_bad_nid():
4747
with pytest.raises(UrnError):
48-
urn = Urn("urn:ngsi*ld:test")
48+
_ = Urn("urn:ngsi*ld:test")
4949

5050

5151
def test_invalid_urn_missing_nss():
5252
with pytest.raises(UrnError):
53-
urn = Urn("urn:ngsi-ld:")
53+
_ = Urn("urn:ngsi-ld:")
5454

5555

5656
def test_guess_from_string():

0 commit comments

Comments
 (0)