-
Notifications
You must be signed in to change notification settings - Fork 40
Extract server apps and funcs from sdk into server
#388
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
59 commits
Select commit
Hold shift + click to select a range
ce642c1
Füge experimentelle Änderungen für Registry und Discovery hinzu
Ornella33 7204ae2
Remove test.py from repository and add it to .gitignore
Ornella33 25cf282
correct discovery server implementation
Ornella33 b68efaa
remove unused code
Ornella33 b590e1c
add in-memory storage and adapt README
Ornella33 1b676e7
change main.py and disccovery.py
Ornella33 7cff8cf
Extract server-related components into server app
zrgt a6577be
Refactor `_get_aas_class_parsers`
zrgt 11c59bc
fix aas_descriptor construct method
Ornella33 6d4aab1
Refactor `read_aas_json_file_into`
zrgt a34230f
Refactor `default()`
zrgt 9079d82
fix method update_from
Ornella33 bd2a9c7
Merge remote-tracking branch 'rwth-iat/Experimental/server_app' into …
zrgt a366538
Refactor `_create_dict()`
zrgt 72297f4
Remove `jsonization._create_dict` as not used
zrgt bd48dec
Split `http.py` into `repository` and `http_api_helpers`
zrgt 6fd1612
Refactor server_model and move create interfaces folder
zrgt eba1d89
Refactor `result_to_xml` and `message_to_xml`
zrgt 4acab0d
Move all response related to `response.py`
zrgt 567b5f1
Create base classes for WSGI apps
zrgt 3d15b51
Refactor `http_api_helpers.py` and `response.py`
zrgt cb107ed
Reformat code with PyCharm
zrgt 4e1c647
Small fixes
zrgt 95b2d5a
Refactor
zrgt b65c420
Refactor
zrgt b0f79d6
Refactor some methods in registry.py and fix some typos
Ornella33 7c8fbe2
remove xmlization for Registry and Discovery classes
Ornella33 eb44e8a
change according to xmlization removal for registry and discovery cla…
Ornella33 d608409
fix error with ServerAASToJSONEncoder
Ornella33 dde2499
Refactor `response.py`
zrgt df38540
Refactor utils
zrgt 1da157f
Rename `server_model` to `model`
zrgt a96da47
correct typos from renaming server_model to model
Ornella33 115db62
Remove discovery/registry related code
zrgt 65d1918
Merge remote-tracking branch 'rwth-iat/develop' into refactor/server
zrgt 0c36396
Add missing code from PR #362
zrgt 6b3c646
Revert changes in .gitignore
zrgt 0a8546e
Fix copyright
zrgt bfd1411
Refactor `test_http.py` to `test_repository.py`
zrgt 1fd76de
fix copyright
Frosty2500 a783066
fix MyPy errors, some tests
Frosty2500 66f3320
fix bugs, reintroduce Identifiable check
Frosty2500 06e51e7
adapt server repository structure
Frosty2500 9d43b69
add MyPy checking, remove codeblock checking
Frosty2500 b6a588e
fix ci pipeline
Frosty2500 de49b3f
adapt static-analysis
Frosty2500 db4e42e
docstrings converter.py
Frosty2500 70d7198
adapt imports, ci; ignore tests for now
Frosty2500 5962862
Use dir() instead of vars() in `Referable.update_from()` (#338)
zrgt 3bf7834
sdk/docs: Move documentation dependencies to pyproject.toml (#389)
moritzsommer d52d66d
fix MyPy issues
Frosty2500 4502c9f
fix versioning
Frosty2500 8f2ef43
adapt lxml, ignore version file
Frosty2500 1721743
adapt versioning
Frosty2500 c19765d
exclude build from MyPy, adapt ci
Frosty2500 168f691
adapt ci
Frosty2500 7e7adfa
add lxml-stubs
Frosty2500 d8b3015
clarify Base64URLConverter
Frosty2500 9bb9067
Add missing newline character at the end of `server/app/pyproject.toml`
s-heppner File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,4 +15,5 @@ python: | |
| install: | ||
| - method: pip | ||
| path: . | ||
| - requirements: docs/add-requirements.txt | ||
| extra_requirements: | ||
| - docs | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,12 +34,13 @@ | |
| import json | ||
| import logging | ||
| import pprint | ||
| from typing import Dict, Callable, ContextManager, TypeVar, Type, List, IO, Optional, Set, get_args | ||
| from typing import (Dict, Callable, ContextManager, TypeVar, Type, | ||
| List, IO, Optional, Set, get_args, Tuple, Iterable, Any) | ||
|
|
||
| from basyx.aas import model | ||
| from .._generic import MODELLING_KIND_INVERSE, ASSET_KIND_INVERSE, KEY_TYPES_INVERSE, ENTITY_TYPES_INVERSE, \ | ||
| IEC61360_DATA_TYPES_INVERSE, IEC61360_LEVEL_TYPES_INVERSE, KEY_TYPES_CLASSES_INVERSE, REFERENCE_TYPES_INVERSE, \ | ||
| DIRECTION_INVERSE, STATE_OF_EVENT_INVERSE, QUALIFIER_KIND_INVERSE, PathOrIO, Path | ||
| DIRECTION_INVERSE, STATE_OF_EVENT_INVERSE, QUALIFIER_KIND_INVERSE, PathOrIO, Path, JSON_AAS_TOP_LEVEL_KEYS_TO_TYPES | ||
|
Comment on lines
41
to
+43
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here. One import per line with |
||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
@@ -154,19 +155,20 @@ def __init__(self, *args, **kwargs): | |
| json.JSONDecoder.__init__(self, object_hook=self.object_hook, *args, **kwargs) | ||
|
|
||
| @classmethod | ||
| def object_hook(cls, dct: Dict[str, object]) -> object: | ||
| # Check if JSON object seems to be a deserializable AAS object (i.e. it has a modelType). Otherwise, the JSON | ||
| # object is returned as is, so it's possible to mix AAS objects with other data within a JSON structure. | ||
| if 'modelType' not in dct: | ||
| return dct | ||
| def _get_aas_class_parsers(cls) -> Dict[str, Callable[[Dict[str, object]], object]]: | ||
s-heppner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| """ | ||
| Returns the dictionary of AAS class parsers. | ||
|
|
||
| The following dict specifies a constructor method for all AAS classes that may be identified using the | ||
| ``modelType`` attribute in their JSON representation. Each of those constructor functions takes the JSON | ||
| representation of an object and tries to construct a Python object from it. Embedded objects that have a | ||
| modelType themselves are expected to be converted to the correct PythonType already. Additionally, each | ||
| function takes a bool parameter ``failsafe``, which indicates weather to log errors and skip defective objects | ||
| instead of raising an Exception. | ||
|
|
||
| # The following dict specifies a constructor method for all AAS classes that may be identified using the | ||
| # ``modelType`` attribute in their JSON representation. Each of those constructor functions takes the JSON | ||
| # representation of an object and tries to construct a Python object from it. Embedded objects that have a | ||
| # modelType themselves are expected to be converted to the correct PythonType already. Additionally, each | ||
| # function takes a bool parameter ``failsafe``, which indicates weather to log errors and skip defective objects | ||
| # instead of raising an Exception. | ||
| AAS_CLASS_PARSERS: Dict[str, Callable[[Dict[str, object]], object]] = { | ||
| :return: The dictionary of AAS class parsers | ||
| """ | ||
| aas_class_parsers: Dict[str, Callable[[Dict[str, object]], object]] = { | ||
| 'AssetAdministrationShell': cls._construct_asset_administration_shell, | ||
| 'AssetInformation': cls._construct_asset_information, | ||
| 'SpecificAssetId': cls._construct_specific_asset_id, | ||
|
|
@@ -189,6 +191,16 @@ def object_hook(cls, dct: Dict[str, object]) -> object: | |
| 'ReferenceElement': cls._construct_reference_element, | ||
| 'DataSpecificationIec61360': cls._construct_data_specification_iec61360, | ||
| } | ||
| return aas_class_parsers | ||
|
|
||
| @classmethod | ||
| def object_hook(cls, dct: Dict[str, object]) -> object: | ||
| # Check if JSON object seems to be a deserializable AAS object (i.e. it has a modelType). Otherwise, the JSON | ||
| # object is returned as is, so it's possible to mix AAS objects with other data within a JSON structure. | ||
| if 'modelType' not in dct: | ||
| return dct | ||
|
|
||
| AAS_CLASS_PARSERS = cls._get_aas_class_parsers() | ||
|
|
||
| # Get modelType and constructor function | ||
| if not isinstance(dct['modelType'], str): | ||
|
|
@@ -799,7 +811,9 @@ def _select_decoder(failsafe: bool, stripped: bool, decoder: Optional[Type[AASFr | |
|
|
||
| def read_aas_json_file_into(object_store: model.AbstractObjectStore, file: PathOrIO, replace_existing: bool = False, | ||
| ignore_existing: bool = False, failsafe: bool = True, stripped: bool = False, | ||
| decoder: Optional[Type[AASFromJsonDecoder]] = None) -> Set[model.Identifier]: | ||
| decoder: Optional[Type[AASFromJsonDecoder]] = None, | ||
| keys_to_types: Iterable[Tuple[str, Any]] = JSON_AAS_TOP_LEVEL_KEYS_TO_TYPES) \ | ||
| -> Set[model.Identifier]: | ||
| """ | ||
| Read an Asset Administration Shell JSON file according to 'Details of the Asset Administration Shell', chapter 5.5 | ||
| into a given object store. | ||
|
|
@@ -817,6 +831,7 @@ def read_aas_json_file_into(object_store: model.AbstractObjectStore, file: PathO | |
| See https://git.rwth-aachen.de/acplt/pyi40aas/-/issues/91 | ||
| This parameter is ignored if a decoder class is specified. | ||
| :param decoder: The decoder class used to decode the JSON objects | ||
| :param keys_to_types: A dictionary of JSON keys to expected types. This is used to check the type of the objects | ||
| :raises KeyError: **Non-failsafe**: Encountered a duplicate identifier | ||
| :raises KeyError: Encountered an identifier that already exists in the given ``object_store`` with both | ||
| ``replace_existing`` and ``ignore_existing`` set to ``False`` | ||
|
|
@@ -843,45 +858,43 @@ def read_aas_json_file_into(object_store: model.AbstractObjectStore, file: PathO | |
| with cm as fp: | ||
| data = json.load(fp, cls=decoder_) | ||
|
|
||
| for name, expected_type in (('assetAdministrationShells', model.AssetAdministrationShell), | ||
| ('submodels', model.Submodel), | ||
| ('conceptDescriptions', model.ConceptDescription)): | ||
| for name, expected_type in keys_to_types: | ||
| try: | ||
| lst = _get_ts(data, name, list) | ||
| except (KeyError, TypeError): | ||
| continue | ||
|
|
||
| for item in lst: | ||
| error_message = "Expected a {} in list '{}', but found {}".format( | ||
| expected_type.__name__, name, repr(item)) | ||
| error_msg = f"Expected a {expected_type.__name__} in list '{name}', but found {repr(item)}." | ||
| if isinstance(item, model.Identifiable): | ||
| if not isinstance(item, expected_type): | ||
| if decoder_.failsafe: | ||
| logger.warning("{} was in wrong list '{}'; nevertheless, we'll use it".format(item, name)) | ||
| else: | ||
| raise TypeError(error_message) | ||
| if not decoder_.failsafe: | ||
| raise TypeError(f"{item} was in the wrong list '{name}'") | ||
| logger.warning(f"{item} was in the wrong list '{name}'; nevertheless, we'll use it") | ||
|
|
||
| if item.id in ret: | ||
| error_message = f"{item} has a duplicate identifier already parsed in the document!" | ||
| error_msg = f"{item} has a duplicate identifier already parsed in the document!" | ||
| if not decoder_.failsafe: | ||
| raise KeyError(error_message) | ||
| logger.error(error_message + " skipping it...") | ||
| raise KeyError(error_msg) | ||
| logger.error(f"{error_msg} Skipping it...") | ||
| continue | ||
|
|
||
| existing_element = object_store.get(item.id) | ||
| if existing_element is not None: | ||
| if not replace_existing: | ||
| error_message = f"object with identifier {item.id} already exists " \ | ||
| f"in the object store: {existing_element}!" | ||
| error_msg = f"Object with id '{item.id}' already exists in store: {existing_element}!" | ||
| if not ignore_existing: | ||
| raise KeyError(error_message + f" failed to insert {item}!") | ||
| logger.info(error_message + f" skipping insertion of {item}...") | ||
| raise KeyError(f"{error_msg} Failed to insert {item}!") | ||
| logger.info(f"{error_msg} Skipping {item}...") | ||
| continue | ||
| object_store.discard(existing_element) | ||
|
|
||
| object_store.add(item) | ||
| ret.add(item.id) | ||
| elif decoder_.failsafe: | ||
| logger.error(error_message) | ||
| logger.error(f"{error_msg} Skipping it...") | ||
| else: | ||
| raise TypeError(error_message) | ||
| raise TypeError(error_msg) | ||
| return ret | ||
|
|
||
|
|
||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you import this many, it is nicer and more readable imo, to put each of them in their own line:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could but we do not do so in the rest of the project. So do you think we should from now always write each in their own line/ change the other imports in the project? I think the current style is also quite readable already.