diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1ce007dc228105849d89d4fc720b9a8bf729ee1b..d95c4620d281693da9e69143c06db82e5796ac14 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,9 +29,9 @@ variables: image: $CI_REGISTRY_IMAGE stages: + - setup - code_style - linting - - setup - test - deploy @@ -53,6 +53,14 @@ pylint: - make lint allow_failure: true +mypy: + tags: [ docker ] + stage: linting + script: + - pip install mypy types-PyYAML types-jsonschema types-requests types-setuptools types-lxml types-python-dateutil + - make mypy + allow_failure: true + # run unit tests unittest_py3.7: tags: [ docker ] @@ -62,7 +70,7 @@ unittest_py3.7: script: &python_test_script # Python docker has problems with tox and pip so use plain pytest here - touch ~/.pylinkahead.ini - - pip install nose pytest pytest-cov python-dateutil jsonschema>=4.4.0 + - pip install pynose pytest pytest-cov jsonschema>=4.4.0 setuptools - pip install . - python -m pytest unittests @@ -100,6 +108,22 @@ unittest_py3.11: image: python:3.11 script: *python_test_script +unittest_py3.12: + tags: [ docker ] + stage: test + needs: [ ] + image: python:3.12 + script: *python_test_script + +unittest_py3.13: + allow_failure: true + tags: [ docker ] + stage: test + needs: [ ] + image: python:3.13-rc + script: *python_test_script + + # Trigger building of server image and integration tests trigger_build: stage: deploy @@ -126,6 +150,7 @@ build-testenv: stage: setup only: - schedules + - web script: - cd unittests/docker - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e2a2501a9ad3c61baea00717c7306f1c35af036..39663cc746c2ff4192d185f3fb303f62e7ef1ac5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added ### +* Support for Python 3.12 + ### Changed ### ### Deprecated ### @@ -17,6 +19,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed ### +* [#104](https://gitlab.com/linkahead/linkahead-pylib/-/issues/104) Selecting + parts of a `Container` with a `slice` used to return a `list` object instead + of a `Container`, removing all useful methods of the `Container` class. This + has been fixed and using a `slice` such as `[:2]` now returns a new + `Container`. + ### Security ### ### Documentation ### diff --git a/Makefile b/Makefile index d15c830d8e4cf6e4bc0b519b9fa5b8cb5f224043..eb767dd4053a2b232d425126358cfb0fd23ffb1c 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ doc: install: @echo "Not implemented yet, use pip for installation." -check: style lint +check: style lint mypy .PHONY: check style: @@ -43,6 +43,10 @@ lint: pylint --unsafe-load-any-extension=y -d all -e E,F src/linkahead/common .PHONY: lint +mypy: + mypy src/linkahead +.PHONY: mypy + unittest: tox -r .PHONY: unittest diff --git a/src/linkahead/common/models.py b/src/linkahead/common/models.py index 4d4692e7279e9208839ba00291e3acb033a02ffb..bb5bd3ccd64a94b47990d37bf86b0e3fedc21182 100644 --- a/src/linkahead/common/models.py +++ b/src/linkahead/common/models.py @@ -3101,6 +3101,14 @@ class Container(list): def __repr__(self): return xml2str(self.to_xml()) + def __getitem__(self, key): + self_as_list_slice = super().__getitem__(key) + if isinstance(self_as_list_slice, list): + # Construct new Container from list slice + return Container().extend(self_as_list_slice) + else: + return self_as_list_slice + @staticmethod def from_xml(xml_str): """Creates a Container from the given xml string. diff --git a/tox.ini b/tox.ini index f62405ef11810c6a2c8e6ee656b562f7c4bd0385..9a862f698573c864921ab9998d1a6a8a07978126 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py37, py38, py39, py310, py311, py312 +envlist=py37, py38, py39, py310, py311, py312, py313 skip_missing_interpreters = true [testenv] @@ -7,6 +7,7 @@ deps = . pynose pytest pytest-cov + mypy jsonschema>=4.4.0 setuptools commands=py.test --cov=caosdb -vv {posargs} @@ -18,3 +19,5 @@ max-line-length=100 testpaths = unittests xfail_strict = True addopts = -x -vv --cov=caosdb +pythonpath = src + diff --git a/unittests/docker/Dockerfile b/unittests/docker/Dockerfile index 9b848cf69c829408f3f3edd599323b6b0321e041..51a9006ff59aad81e3f2fc09b5d783518a07f06e 100644 --- a/unittests/docker/Dockerfile +++ b/unittests/docker/Dockerfile @@ -12,4 +12,4 @@ ARG COMMIT="dev" # TODO Rename to linkahead RUN git clone -b dev https://gitlab.indiscale.com/caosdb/src/caosdb-pylib.git linkahead-pylib && \ cd linkahead-pylib && git checkout $COMMIT && pip3 install . -RUN pip3 install recommonmark sphinx-rtd-theme +RUN pip3 install recommonmark sphinx-rtd-theme mypy diff --git a/unittests/test_container.py b/unittests/test_container.py index 4cd8fefcaefee9fe6fdc5857805353227b493dfb..c3a60140d43383c81f03c38c9dd5cc7779bc77ba 100644 --- a/unittests/test_container.py +++ b/unittests/test_container.py @@ -5,7 +5,8 @@ # This file is a part of the LinkAhead Project. # # Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com> -# Copyright (C) 2020 IndiScale GmbH <info@indiscale.com> +# Copyright (C) 2024 Joscha Schmiedt <joscha@schmiedt.dev> +# Copyright (C) 2020-2024 IndiScale GmbH <info@indiscale.com> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -178,3 +179,23 @@ def test_container_deletion_with_references(): assert len(deps14a) == 1 and deps14a.pop() == -1 assert len(deps14b) == 1 and deps14b.pop() == -1 assert len(deps15) == 1 and deps15.pop() == -1 + + +def test_container_slicing(): + cont = db.Container() + cont.extend([db.Record(name=f"TestRec{ii+1}") for ii in range(5)]) + assert isinstance(cont, db.common.models.Container) + container_slice = cont[:2] + assert isinstance(container_slice, db.common.models.Container), \ + f"Container slice should be Container, was {type(container_slice)}" + for element in container_slice: + assert isinstance(element, db.Record), \ + f"element in slice was not Record, but {type(element)}" + assert len(container_slice) == 2 + assert cont[-1].name == "TestRec5" + + with pytest.raises(TypeError): + cont["stringkey"] + + with pytest.raises(TypeError): + cont[[0, 2, 3]]