diff --git a/.gitlab/issue_templates/Default.md b/.gitlab/issue_templates/Default.md new file mode 100644 index 0000000000000000000000000000000000000000..aa1a65aca363b87aff50280e1a86824009d2098b --- /dev/null +++ b/.gitlab/issue_templates/Default.md @@ -0,0 +1,28 @@ +## Summary + +*Please give a short summary of what the issue is.* + +## Expected Behavior + +*What did you expect how the software should behave?* + +## Actual Behavior + +*What did the software actually do?* + +## Steps to Reproduce the Problem + +*Please describe, step by step, how others can reproduce the problem. Please try these steps for yourself on a clean system.* + +1. +2. +3. + +## Specifications + +- Version: *Which version of this software?* +- Platform: *Which operating system, which other relevant software versions?* + +## Possible fixes + +*Do you have ideas how the issue can be resolved?* diff --git a/.gitlab/merge_request_templates/Default.md b/.gitlab/merge_request_templates/Default.md new file mode 100644 index 0000000000000000000000000000000000000000..35c6d01c5904289b77fc7f1de9419ef91a1510e9 --- /dev/null +++ b/.gitlab/merge_request_templates/Default.md @@ -0,0 +1,54 @@ +# Summary + +*Insert a meaningful description for this merge request here: What is the new/changed behavior? +Which bug has been fixed? Are there related issues?* + + +# Focus + +*Point the reviewer to the core of the code change. Where should they start reading? What should +they focus on (e.g. security, performance, maintainability, user-friendliness, compliance with the +specs, finding more corner cases, concrete questions)?* + + +# Test Environment + +*How to set up a test environment for manual testing?* + + +# Check List for the Author + +Please, prepare your MR for a review. Be sure to write a summary and a focus and create gitlab +comments for the reviewer. They should guide the reviewer through the changes, explain your changes +and also point out open questions. For further good practices have a look at [our review +guidelines](https://gitlab.com/caosdb/caosdb/-/blob/dev/REVIEW_GUIDELINES.md) + +- [ ] All automated tests pass +- [ ] Reference related issues +- [ ] Up-to-date CHANGELOG.md (or not necessary) +- [ ] Up-to-date JSON schema (or not necessary) +- [ ] Appropriate user and developer documentation (or not necessary) + - How do I use the software? Assume "stupid" users. + - How do I develop or debug the software? Assume novice developers. +- [ ] Annotations in code (Gitlab comments) + - Intent of new code + - Problems with old code + - Why this implementation? + + +# Check List for the Reviewer + +- [ ] I understand the intent of this MR +- [ ] All automated tests pass +- [ ] Up-to-date CHANGELOG.md (or not necessary) +- [ ] Appropriate user and developer documentation (or not necessary) +- [ ] The test environment setup works and the intended behavior is reproducible in the test + environment +- [ ] In-code documentation and comments are up-to-date. +- [ ] Check: Are there specifications? Are they satisfied? + +For further good practices have a look at [our review guidelines](https://gitlab.com/caosdb/caosdb/-/blob/dev/REVIEW_GUIDELINES.md). + + +/assign me +/target_branch dev diff --git a/CHANGELOG.md b/CHANGELOG.md index b7e37c52b9d734da4e877fe6456fb5dee6e02a37..f3c6a023893526d8727858562a87453fdcaed60a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.11.1] - 2023-03-07 ## +(Florian Spreckelsen) + +### Changed ### + +* Renamed `caosdb.common.models._Parents` to `caosdb.common.models._ParentList`. + +### Fixed ### + +* [caosdb-pylib#90](https://gitlab.com/caosdb/caosdb-pylib/-/issues/90): `Entity.get_parents_recursively()` did not work for unretrieved parents. + ## [0.11.0] - 2023-01-19 ## (Florian Spreckelsen) diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000000000000000000000000000000000000..910e40a2193d527fc8e4eb68c4ca6b10a28d3630 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,25 @@ +cff-version: 1.2.0 +message: "If you use this software, please cite it as below." +authors: + - family-names: Fitschen + given-names: Timm + orcid: https://orcid.org/0000-0002-4022-432X + - family-names: Schlemmer + given-names: Alexander + orcid: https://orcid.org/0000-0003-4124-9649 + - family-names: Hornung + given-names: Daniel + orcid: https://orcid.org/0000-0002-7846-6375 + - family-names: tom Wörden + given-names: Henrik + orcid: https://orcid.org/0000-0002-5549-578X + - family-names: Parlitz + given-names: Ulrich + orcid: https://orcid.org/0000-0003-3058-1435 + - family-names: Luther + given-names: Stefan + orcid: https://orcid.org/0000-0001-7214-8125 +title: CaosDB - Pylib +version: 0.11.1 +doi: 10.3390/data4020083 +date-released: 2022-11-14 \ No newline at end of file diff --git a/RELEASE_GUIDELINES.md b/RELEASE_GUIDELINES.md index 863afb8f3ac7d6770c372620523638b900785227..95ee8e314871153476c30790a456242e38dcaf9e 100644 --- a/RELEASE_GUIDELINES.md +++ b/RELEASE_GUIDELINES.md @@ -24,6 +24,7 @@ guidelines of the CaosDB Project - `version` variables in `src/doc/conf.py` - Version on [setup.py](./setup.py): Check the `MAJOR`, `MINOR`, `MICRO`, `PRE` variables and set `ISRELEASED` to `True`. Use the possibility to issue pre-release versions for testing. + - `CITATION.cff` (update version and date) 5. Merge the release branch into the main branch. diff --git a/setup.py b/setup.py index 0d2305f0b59453c0d209918fc74b991f87bcbda0..a281a7b67182c19b984846f498bdd99a3a2d6dc5 100755 --- a/setup.py +++ b/setup.py @@ -48,7 +48,7 @@ from setuptools import find_packages, setup ISRELEASED = True MAJOR = 0 MINOR = 11 -MICRO = 0 +MICRO = 1 # Do not tag as pre-release until this commit # https://github.com/pypa/packaging/pull/515 # has made it into a release. Probably we should wait for pypa/packaging>=21.4 diff --git a/src/caosdb/common/models.py b/src/caosdb/common/models.py index 83359ac847fa62de976208f9af023a2cf2a73af6..08fcd0206b9df22902e80277d0e57b5f67c76db5 100644 --- a/src/caosdb/common/models.py +++ b/src/caosdb/common/models.py @@ -34,6 +34,7 @@ All additional classes are either important for the entities or the transactions. """ from __future__ import print_function, unicode_literals +from __future__ import annotations # Can be removed with 3.10. import re import sys @@ -82,7 +83,7 @@ SPECIAL_ATTRIBUTES = ["name", "role", "datatype", "description", "id", "path", "checksum", "size"] -class Entity(object): +class Entity: """Entity is a generic CaosDB object. @@ -101,6 +102,8 @@ class Entity(object): self._checksum = None self._size = None self._upload = None + # If an entity is used (e.g. as parent), it is wrapped instead of being used directly. + # see Entity._wrap() self._wrapped_entity = None self._version = None self._cuid = None @@ -111,7 +114,7 @@ class Entity(object): self.value = value self.messages = _Messages() self.properties = _Properties() - self.parents = _Parents() + self.parents = _ParentList() self.path = None self.file = None self.unit = None @@ -530,7 +533,7 @@ class Entity(object): value=value, unit=unit) if abstract_property is not None: - new_property._wrap(property) + new_property._wrap(abstract_property) # FIXME: this really necessary? @@ -621,26 +624,46 @@ class Entity(object): return self - def has_parent(self, parent, recursive=True, - check_name=True, check_id=False): - """Checks if this entity has a given parent. + def has_parent(self, parent: Entity, recursive: bool = True, retrieve: bool = True, + check_name: bool = True, check_id: bool = False): + """Check if this entity has a given parent. If 'check_name' and 'check_id' are both False, test for identity on the Python level. Otherwise use the name and/or ID for the check. Note that, if checked, name or ID should not be None, lest the check fail. - @param parent: Check for this parent. - @param recursive: Whether to check recursively. - @param check_name: Whether to use the name for ancestry check. - @param check_id: Whether to use the ID for ancestry check. - @return: True if 'parent' is a true parent, False otherwise. - """ +Parameters +---------- + +parent: Entity + Check for this parent. + +recursive: bool, optional + Whether to check recursively. + +check_name: bool, optional + Whether to use the name for ancestry check. + +check_id: bool, optional + Whether to use the ID for ancestry check. + +retrieve: bool, optional + If False, do not retrieve parents from the server. + +Returns +------- +out: bool + True if ``parent`` is a true parent, False otherwise. +""" if recursive: - parents = self.get_parents_recursively() + parents = self.get_parents_recursively(retrieve=retrieve) else: - parents = [pp._wrapped_entity for pp in self.parents] + if retrieve: + parents = [pp.retrieve()._wrapped_entity for pp in self.parents] + else: + parents = [pp._wrapped_entity for pp in self.parents] if not (check_name or check_id): return parent in parents @@ -659,39 +682,61 @@ class Entity(object): def get_parents(self): """Get all parents of this entity. - @return: _Parents(list) + @return: _ParentList(list) """ return self.parents - def get_parents_recursively(self): + def get_parents_recursively(self, retrieve: bool = True): """Get all ancestors of this entity. - @return: list of Entities - """ +Parameters +---------- + +retrieve: bool, optional + If False, do not retrieve parents from the server. + +Returns +------- +out: List[Entity] + The parents of this Entity +""" - all_parents = _Parents() - self._get_parent_recursively(all_parents) + all_parents = [] + self._get_parent_recursively(all_parents, retrieve=retrieve) return all_parents - def _get_parent_recursively(self, all_parents): + def _get_parent_recursively(self, all_parents: list, retrieve: bool = True): """Get all ancestors with a little helper. As a side effect of this method, the ancestors are added to all_parents. - @param all_parents: The added parents so far. + @param all_parents: list, The added parents so far. @return: None, but see side effects. """ for parent in self.parents: + # TODO: + # Comment on _wrap and _wrapped_entity + # Currently, I (henrik) do not why the wrapping is necessary (and it is not + # documented). However, the following illustrates, why I think, it is a bad idea. + # First you add a parent with rec.add_parent(parent), but then you cannot access + # attributes of parent when you use rec.parents[0] for example becasue you do not get + # the same object but a wrapping object and you need to know that you only get the + # original by accessing the private (!) _wrapped_entity object. w_parent = parent._wrapped_entity + if retrieve: + parent.retrieve() + for next_parent in parent.parents: + w_parent.add_parent(next_parent) - if w_parent not in all_parents: + if (w_parent.id, w_parent.name) not in [ + (all_p.id, all_p.name) for all_p in all_parents]: all_parents.append(w_parent) - w_parent._get_parent_recursively(all_parents) + w_parent._get_parent_recursively(all_parents, retrieve=retrieve) def get_parent(self, key): """Return the first parent matching the key or None if no match exists. @@ -1135,7 +1180,7 @@ class Entity(object): else: raise TypeError( 'Child was neither a Property, nor a Parent, nor a Message.\ - Was ' + str(type(child))) + Was ' + str(type(child)) + "\n" + str(child)) # add VALUE value = None @@ -1296,6 +1341,12 @@ class Entity(object): flags=flags)[0] def _wrap(self, entity): + """ + When entity shall be used as parent or property it is not added to the corresponding list + (such as the parent list) directly, but another Entity object is created and the original + Entity is wrapped using this function + TODO: document here and in dev docs why this is done. + """ self._wrapped_entity = entity return self @@ -2094,7 +2145,8 @@ class _Properties(list): raise KeyError(str(prop) + " not found.") -class _Parents(list): +class _ParentList(list): + # TODO unclear why this class is private. Isn't it use full for users? def _get_entity_by_cuid(self, cuid): ''' @@ -2697,9 +2749,11 @@ class Container(list): elif isinstance(entity, QueryTemplate): super().append(entity) else: - raise TypeError( - "Entity was neither an id nor a name nor an entity." + - " (was " + str(type(entity)) + ")") + warn("Entity was neither an id nor a name nor an entity." + + " (was " + str(type(entity)) + ":\n" + str(entity) + ")") + # raise TypeError( + # "Entity was neither an id nor a name nor an entity." + + # " (was " + str(type(entity)) + "\n" + str(entity) + ")") return self @@ -3647,6 +3701,7 @@ class Container(list): for p in e.get_properties(): if p.id is None: if p.name is not None: + # TODO using try except for normal execution flow is bad style try: w = self.get_entity_by_name(p.name) p._wrap(w) @@ -3658,6 +3713,7 @@ class Container(list): for p in e.get_parents(): if p.id is None: if p.name is not None: + # TODO using try except for normal execution flow is bad style try: p._wrap(self.get_entity_by_name(p.name)) except KeyError: diff --git a/src/caosdb/connection/connection.py b/src/caosdb/connection/connection.py index a2802848af7bae8fb65378532156d8469f31a9b8..46dadea9dfcfa6e614493b75d709f604aa188ef6 100644 --- a/src/caosdb/connection/connection.py +++ b/src/caosdb/connection/connection.py @@ -287,6 +287,8 @@ class _DefaultCaosDBServerConnection(CaosDBServerConnection): "Insecure SSL mode, certificate will not be checked! " "Please consider removing the `ssl_insecure` configuration option.\n" "****************") + warnings.filterwarnings(action="ignore", module="urllib3", + message="Unverified HTTPS request is being made") verify = False if verify is not None: self._session.verify = verify diff --git a/src/doc/conf.py b/src/doc/conf.py index b6d82cd82ee62e6a03c2fdea8c35129adb6096a9..3168916a86d208417ce580d43ce8ae952dde930d 100644 --- a/src/doc/conf.py +++ b/src/doc/conf.py @@ -29,10 +29,10 @@ copyright = '2022, IndiScale GmbH' author = 'Daniel Hornung' # The short X.Y version -version = '0.11.0' +version = '0.11.1' # The full version, including alpha/beta/rc tags # release = '0.5.2-rc2' -release = '0.10.1' +release = '0.11.1' # -- General configuration ---------------------------------------------------