diff --git a/aas/model/__init__.py b/aas/model/__init__.py index a890f88aa9d89ee3acabce87a1befd9f8c3aa822..9e585582f2cc397c482a0c3749f6870644f2f6bb 100644 --- a/aas/model/__init__.py +++ b/aas/model/__init__.py @@ -9,9 +9,8 @@ base.py Basic structures of the model, including all abstract classes and enumerations. This provides inheritance for the higher level structures. -registry.py - Registries for the AAS, in order to enable resolving global identifiers; and mapping identifiers to identifiable - objects. +provider.py + Providers for AAS objects, in order to store and retrieve identifiable objects by their Identifier. security.py Security model of the AAS. Currently not existing. @@ -26,7 +25,7 @@ from .aas import * from .security import * from .base import * from .submodel import * -from .registry import * +from .provider import * # A mapping of PyI40AAS implementation classes to the corresponding `KeyElements` enum members for all classes that are # covered by this enum. diff --git a/aas/model/base.py b/aas/model/base.py index 221c5efc7a087e29ce42eb4c66170684aa2db550..d610d576c8f579a961d24d2fa6ab48948294c6da 100644 --- a/aas/model/base.py +++ b/aas/model/base.py @@ -22,7 +22,7 @@ from typing import List, Optional, Set, TypeVar, MutableSet, Generic, Iterable, import re if TYPE_CHECKING: - from . import registry + from . import provider DataTypeDef = str # any xsd simple type as string BlobType = bytes @@ -548,7 +548,7 @@ class AASReference(Reference, Generic[_RT]): self.type: Type[_RT] object.__setattr__(self, 'type', type_) - def resolve(self, registry_: "registry.AbstractRegistry") -> _RT: + def resolve(self, provider_: "provider.AbstractObjectProvider") -> _RT: """ Follow the reference and retrieve the Referable object it points to @@ -573,10 +573,10 @@ class AASReference(Reference, Generic[_RT]): resolved_keys: List[str] = [] # for more helpful error messages - # First, resolve the identifier-key via the registry + # First, resolve the identifier-key via the provider identifier: Identifier = self.key[last_identifier_index].get_identifier() # type: ignore try: - item = registry_.get_identifiable(identifier) + item = provider_.get_identifiable(identifier) except KeyError as e: raise KeyError("Could not resolve global reference key {}".format(identifier)) from e resolved_keys.append(str(identifier)) diff --git a/aas/model/registry.py b/aas/model/provider.py similarity index 67% rename from aas/model/registry.py rename to aas/model/provider.py index b77a9c2cbf239a34cd882e545191db6b87b943b8..04b28cd1090210dff4502ccc21dcd3cb17163a5e 100644 --- a/aas/model/registry.py +++ b/aas/model/provider.py @@ -19,15 +19,20 @@ from typing import MutableSet, Iterator, Generic, TypeVar, Dict, List, Optional from .base import Identifier, Identifiable -class AbstractRegistry(metaclass=abc.ABCMeta): +class AbstractObjectProvider(metaclass=abc.ABCMeta): """ - Abstract baseclass for registries and registry proxy objects, that allow to resolve global identifiers to - Identifiable objects (resp. proxy objects for remote Identifiable objects). + Abstract baseclass for all objects, that allow to retrieve Identifiable objects (resp. proxy objects for remote + Identifiable objects) by their Identifier. + + This includes local object stores, database clients and AAS API clients. """ @abc.abstractmethod def get_identifiable(self, identifier: Identifier) -> Identifiable: """ - Find a Referable in this Namespaces by its id_short + Find an Identifiable by its id_short + + This may include looking up the object's endpoint in a registry and fetching it from an HTTP server or a + database. :param identifier: :return: The Identifiable object (or a proxy object for a remote Identifiable object) @@ -39,10 +44,13 @@ class AbstractRegistry(metaclass=abc.ABCMeta): _IT = TypeVar('_IT', bound=Identifiable) -class AbstractObjectStore(AbstractRegistry, MutableSet[_IT], Generic[_IT], metaclass=abc.ABCMeta): +class AbstractObjectStore(AbstractObjectProvider, MutableSet[_IT], Generic[_IT], metaclass=abc.ABCMeta): """ - Abstract baseclass of for local containers for storage of Identifiable objects, that can be used as Registry to - retrieve the stored objects by Identifier. + Abstract baseclass of for container-like objects for storage of Identifiable objects. + + ObjectStores are special ObjectProvides that – in addition to retrieving objects by Identifier – allow to add and + delete objects (i.e. behave like a Python set). This includes local object stores (like `DictObjectStore`) and + database clients. """ pass @@ -81,23 +89,23 @@ class DictObjectStore(AbstractObjectStore[_IT], Generic[_IT]): return iter(self._backend.values()) -class RegistryMultiplexer(AbstractRegistry): +class ObjectProviderMultiplexer(AbstractObjectProvider): """ A multiplexer for Registries of Identifiable objects. This class combines multiple Registries of Identifiable objects into a single one to allow retrieving Identifiable - objects from different sources. It implements the AbstractRegistry interface to be used as Registry itself. + objects from different sources. It implements the AbstractObjectProvider interface to be used as Registry itself. :ivar registries: A list of registries to query when looking up an object """ - def __init__(self, registries: Optional[List[AbstractRegistry]] = None): - self.registries: List[AbstractRegistry] = registries if registries is not None else [] + def __init__(self, registries: Optional[List[AbstractObjectProvider]] = None): + self.providers: List[AbstractObjectProvider] = registries if registries is not None else [] def get_identifiable(self, identifier: Identifier) -> Identifiable: - for registry in self.registries: + for provider in self.providers: try: - return registry.get_identifiable(identifier) + return provider.get_identifiable(identifier) except KeyError: pass raise KeyError("Identifier could not be found in any of the {} consulted registries." - .format(len(self.registries))) + .format(len(self.providers))) diff --git a/aas/util/identification.py b/aas/util/identification.py index 1b579e79df8171e2c32dec2628f57a405f5251a2..1d084394df426b89ca0f9d51c183f23444d7fe28 100644 --- a/aas/util/identification.py +++ b/aas/util/identification.py @@ -68,17 +68,17 @@ class NamespaceIRIGenerator(AbstractIdentifierGenerator): existence of the identification is checked by querying the given Registry. If a collision is detected, a number is prepended """ - def __init__(self, namespace: str, registry: model.AbstractRegistry): + def __init__(self, namespace: str, provider: model.AbstractObjectProvider): """ Create a new NamespaceIRIGenerator :param namespace: The IRI Namespace to generate Identifications in. It must be a valid IRI (starting with a scheme) and end on either #, /, or = to form a reasonable namespace. - :param registry: An AbstractRegistry to check existence of Identifiers + :param provider: An AbstractObjectProvider to check existence of Identifiers """ super().__init__() if not re.match(r'^[a-zA-Z][a-zA-Z0-9+\-\.]*:.*[#/=]$', namespace): raise ValueError("Namespace must be a valid IRI, ending with #, / or =") - self.registry = registry + self.provider = provider self._namespace = namespace self._counter_cache: Dict[str, int] = {} @@ -96,9 +96,9 @@ class NamespaceIRIGenerator(AbstractIdentifierGenerator): iri = "{}{}{}{:04d}".format(self._namespace, proposal, "_" if proposal else "", counter) else: iri = "{}{}".format(self._namespace, proposal) - # Try to find iri in registry. If it does not exist (KeyError), we found a unique one to return + # Try to find iri in provider. If it does not exist (KeyError), we found a unique one to return try: - self.registry.get_identifiable(model.Identifier(iri, model.IdentifierType.IRI)) + self.provider.get_identifiable(model.Identifier(iri, model.IdentifierType.IRI)) except KeyError: self._counter_cache[proposal] = counter return model.Identifier(iri, model.IdentifierType.IRI) diff --git a/test/model/test_base.py b/test/model/test_base.py index fce70061c46357f9971878063508e9edd6ffe0b7..1dbe795aa6d51fcc79708b4aa7518aeb720e15ab 100644 --- a/test/model/test_base.py +++ b/test/model/test_base.py @@ -180,13 +180,13 @@ class AASReferenceTest(unittest.TestCase): def test_reference_typing(self) -> None: dummy_submodel = model.Submodel(model.Identifier("urn:x-test:x", model.IdentifierType.IRI)) - class DummyRegistry(model.AbstractRegistry): + class DummyObjectProvider(model.AbstractObjectProvider): def get_identifiable(self, identifier: Identifier) -> Identifiable: return dummy_submodel x = model.AASReference((model.Key(model.KeyElements.SUBMODEL, False, "urn:x-test:x", model.KeyType.IRI),), model.Submodel) - submodel: model.Submodel = x.resolve(DummyRegistry()) + submodel: model.Submodel = x.resolve(DummyObjectProvider()) self.assertIs(submodel, submodel) def test_resolve(self) -> None: @@ -196,7 +196,7 @@ class AASReferenceTest(unittest.TestCase): submodel = model.Submodel(model.Identifier("urn:x-test:submodel", model.IdentifierType.IRI), {collection}) collection.parent = submodel - class DummyRegistry(model.AbstractRegistry): + class DummyObjectProvider(model.AbstractObjectProvider): def get_identifiable(self, identifier: Identifier) -> Identifiable: if identifier == submodel.identification: return submodel @@ -209,7 +209,7 @@ class AASReferenceTest(unittest.TestCase): model.KeyType.IDSHORT), model.Key(model.KeyElements.PROPERTY, False, "prop", model.KeyType.IDSHORT)), model.Property) - self.assertIs(prop, ref1.resolve(DummyRegistry())) + self.assertIs(prop, ref1.resolve(DummyObjectProvider())) ref2 = model.AASReference((model.Key(model.KeyElements.SUBMODEL, False, "urn:x-test:submodel", model.KeyType.IRI), @@ -219,7 +219,7 @@ class AASReferenceTest(unittest.TestCase): model.Key(model.KeyElements.PROPERTY, False, "prop", model.KeyType.IDSHORT)), model.Property) with self.assertRaises(TypeError): - ref2.resolve(DummyRegistry()) + ref2.resolve(DummyObjectProvider()) with self.assertRaises(AttributeError): ref1.key[2].value = "prop1" @@ -228,7 +228,7 @@ class AASReferenceTest(unittest.TestCase): model.Property) # Oh no, yet another typo! with self.assertRaises(KeyError): - ref3.resolve(DummyRegistry()) + ref3.resolve(DummyObjectProvider()) ref4 = model.AASReference((model.Key(model.KeyElements.SUBMODEL, False, "urn:x-test:submodel", model.KeyType.IRI),), @@ -236,7 +236,7 @@ class AASReferenceTest(unittest.TestCase): # Okay, typo is fixed, but the type is not what we expect. However, we should get the the submodel via the # exception's value attribute with self.assertRaises(model.UnexpectedTypeError) as cm: - ref4.resolve(DummyRegistry()) + ref4.resolve(DummyObjectProvider()) self.assertIs(submodel, cm.exception.value) def test_from_referable(self) -> None: diff --git a/test/model/test_registry.py b/test/model/test_registry.py index d227fe84ad50036b70297f802fbec1fb15a424ac..afc546a42aefe2621fdbb20dff5607ac310772af 100644 --- a/test/model/test_registry.py +++ b/test/model/test_registry.py @@ -36,7 +36,7 @@ class RegistriesTest(unittest.TestCase): self.assertIs(self.aas2, object_store.pop()) self.assertEqual(0, len(object_store)) - def test_registry_multiplexer(self) -> None: + def test_provider_multiplexer(self) -> None: aas_object_store: model.DictObjectStore[model.AssetAdministrationShell] = model.DictObjectStore() aas_object_store.add(self.aas1) aas_object_store.add(self.aas2) @@ -44,7 +44,7 @@ class RegistriesTest(unittest.TestCase): submodel_object_store.add(self.submodel1) submodel_object_store.add(self.submodel2) - multiplexer = model.RegistryMultiplexer([aas_object_store, submodel_object_store]) + multiplexer = model.ObjectProviderMultiplexer([aas_object_store, submodel_object_store]) self.assertIs(self.aas1, multiplexer.get_identifiable(model.Identifier("urn:x-test:aas1", model.IdentifierType.IRI))) self.assertIs(self.submodel1, diff --git a/test/util/test_identification.py b/test/util/test_identification.py index 14a629c1dd2705750c38f5683be4355003381cec..b6e244ad5325553c010ea0f68f5a404a8b3fd3fb 100644 --- a/test/util/test_identification.py +++ b/test/util/test_identification.py @@ -28,32 +28,32 @@ class IdentifierGeneratorTest(unittest.TestCase): ids.add(identification) def test_generate_iri_identifier(self): - registry = model.DictObjectStore() + provider = model.DictObjectStore() # Check expected Errors when Namespaces are not valid with self.assertRaises(ValueError): - generator = NamespaceIRIGenerator("", registry) + generator = NamespaceIRIGenerator("", provider) with self.assertRaises(ValueError): - generator = NamespaceIRIGenerator("http", registry) + generator = NamespaceIRIGenerator("http", provider) - generator = NamespaceIRIGenerator("http://acplt.org/AAS/", registry) + generator = NamespaceIRIGenerator("http://acplt.org/AAS/", provider) self.assertEqual("http://acplt.org/AAS/", generator.namespace) identification = generator.generate_id() self.assertEqual(identification.id, "http://acplt.org/AAS/0000") - registry.add(model.Submodel(identification)) + provider.add(model.Submodel(identification)) for i in range(10): identification = generator.generate_id() - self.assertNotIn(identification, registry) - registry.add(model.Submodel(identification)) + self.assertNotIn(identification, provider) + provider.add(model.Submodel(identification)) self.assertEqual(identification.id, "http://acplt.org/AAS/0010") identification = generator.generate_id("Spülmaschine") self.assertEqual(identification.id, "http://acplt.org/AAS/Spülmaschine") - registry.add(model.Submodel(identification)) + provider.add(model.Submodel(identification)) for i in range(10): identification = generator.generate_id("Spülmaschine") - self.assertNotIn(identification, registry) + self.assertNotIn(identification, provider) self.assertNotEqual(identification.id, "http://acplt.org/AAS/Spülmaschine")