Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
98ad8cd
🧪 Ensure subdirectory in executor tests
kzscisoft Jun 20, 2025
17afc8c
⚡️ Add check that deleted folder is returned, also add some performan…
kzscisoft Jun 20, 2025
4551b7a
Merge branch 'kzscisoft/fix-folder-deletion-inconsistencies' into kzs…
kzscisoft Jun 20, 2025
e08aee1
⚡️ Added __repr__ to LLAPI classes
kzscisoft Jun 20, 2025
f473a2b
⚡️ Add error raise for folder duplicate
kzscisoft Jun 20, 2025
6abba88
🐛 Ensure folder ID retrieval arguments are passed
kzscisoft Jun 20, 2025
a6650bc
📝 Updated CHANGELOG with bug fixes
kzscisoft Jun 20, 2025
a5eaf18
🐛 Fix check of folder deletion
kzscisoft Jun 20, 2025
2f44e61
🧪 Correct folder deletion test
kzscisoft Jun 20, 2025
9c0c71f
🐛 Use daemon=True in Thread creation
kzscisoft Jun 20, 2025
aff6a90
👷 Separate CI further
kzscisoft Jun 20, 2025
aab9e24
💚 Fix CI run categories
kzscisoft Jun 20, 2025
506fe19
🧪 Modify emission metrics test
kzscisoft Jun 20, 2025
4725b54
🧪 Use stderr and stdout in processes test for conclusion
kzscisoft Jun 20, 2025
97a429d
🧪 Set metric call counter in top level fixture
kzscisoft Jun 20, 2025
256578d
🧪 Remove sleeps where possible in tests
kzscisoft Jun 21, 2025
f139788
🧪 Use non-zero file size as test completion
kzscisoft Jun 23, 2025
7b9aed5
🧪 Switch to BASH based process test
kzscisoft Jun 23, 2025
ebeedec
🐛 Set all threads to be daemon=True
kzscisoft Jun 24, 2025
4268676
🧪 Ensure at least one emission metric sent
kzscisoft Jun 24, 2025
e3cf626
📝 Updated CHANGELOG
kzscisoft Jun 24, 2025
9566430
🥅 Disallow user to call close on Run within context manager
kzscisoft Jun 24, 2025
4876bf1
🧪 Further test improvements and formatting
kzscisoft Jun 24, 2025
c4f2810
🥅 Use ObjectNotFoundError during folder deletion
kzscisoft Jun 25, 2025
973900f
🧪 Improve test consistency
kzscisoft Jun 25, 2025
a7ae97a
✅ Fix closing of run during test
kzscisoft Jun 25, 2025
8f37b99
👷 Split Client tests in CI
kzscisoft Jun 25, 2025
9e70cb5
🧪 Add test markers to pyproject.toml
kzscisoft Jun 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 98 additions & 30 deletions .github/workflows/test_client_ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ concurrency:
cancel-in-progress: true

jobs:
online_unit_tests:
object_retrieval:
runs-on: ubuntu-latest
timeout-minutes: 40
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.13
Expand All @@ -40,12 +40,12 @@ jobs:
SIMVUE_URL: ${{ secrets.SIMVUE_URL }}
SIMVUE_TOKEN: ${{ secrets.SIMVUE_TOKEN }}
run: >-
python -m pytest tests/unit/ -x
-m online -c /dev/null -p no:warnings
python -m pytest -x
-m object_retrieval -c /dev/null -p no:warnings
-n 0 -v -o cache_dir=${GITHUB_WORKSPACE}/.pytest-cache
offline_unit_tests:
object_removal:
runs-on: ubuntu-latest
timeout-minutes: 40
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.13
Expand All @@ -65,12 +65,12 @@ jobs:
SIMVUE_URL: ${{ secrets.SIMVUE_URL }}
SIMVUE_TOKEN: ${{ secrets.SIMVUE_TOKEN }}
run: >-
python -m pytest tests/unit/ -x
-m offline -c /dev/null -p no:warnings
python -m pytest -x
-m object_removal -c /dev/null -p no:warnings
-n 0 -v -o cache_dir=${GITHUB_WORKSPACE}/.pytest-cache
online_functional_tests:
dispatch_tests:
runs-on: ubuntu-latest
timeout-minutes: 40
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.13
Expand All @@ -82,20 +82,19 @@ jobs:
python -m pip install poetry
poetry self add poetry-plugin-export
poetry export -f requirements.txt --with dev -o requirements.txt --all-extras
python -m pip install torch --index-url https://download.pytorch.org/whl/cpu
python -m pip install -r requirements.txt
python -m pip install .
- name: Test with pytest
env:
SIMVUE_URL: ${{ secrets.SIMVUE_URL }}
SIMVUE_TOKEN: ${{ secrets.SIMVUE_TOKEN }}
run: >-
python -m pytest tests/functional/ -x
-m online -m "not eco" -c /dev/null -p no:warnings
python -m pytest -x
-m dispatch -c /dev/null -p no:warnings
-n 0 -v -o cache_dir=${GITHUB_WORKSPACE}/.pytest-cache
offline_functional_tests:
run_tests_online:
runs-on: ubuntu-latest
timeout-minutes: 40
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.13
Expand All @@ -115,12 +114,12 @@ jobs:
SIMVUE_URL: ${{ secrets.SIMVUE_URL }}
SIMVUE_TOKEN: ${{ secrets.SIMVUE_TOKEN }}
run: >-
python -m pytest tests/functional/ -x
-m offline -c /dev/null -p no:warnings
python -m pytest -x
-m run -m online -c /dev/null -p no:warnings
-n 0 -v -o cache_dir=${GITHUB_WORKSPACE}/.pytest-cache
other_unit_tests:
run_tests_offline:
runs-on: ubuntu-latest
timeout-minutes: 40
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.13
Expand All @@ -140,13 +139,84 @@ jobs:
SIMVUE_URL: ${{ secrets.SIMVUE_URL }}
SIMVUE_TOKEN: ${{ secrets.SIMVUE_TOKEN }}
run: >-
python -m pytest tests/unit/ -x
-m 'not offline' -m 'not online'
-m 'not scenario' -c /dev/null
-p no:warnings -n 0 -v -o cache_dir=${GITHUB_WORKSPACE}/.pytest-cache
other_functional_tests:
python -m pytest -x
-m run -m offline -c /dev/null -p no:warnings
-n 0 -v -o cache_dir=${GITHUB_WORKSPACE}/.pytest-cache
config_tests:
runs-on: ubuntu-latest
timeout-minutes: 40
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.13
uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install dependencies
run: |
python -m pip install poetry
poetry self add poetry-plugin-export
poetry export -f requirements.txt --with dev -o requirements.txt --all-extras
python -m pip install -r requirements.txt
python -m pip install .
- name: Test with pytest
env:
SIMVUE_URL: ${{ secrets.SIMVUE_URL }}
SIMVUE_TOKEN: ${{ secrets.SIMVUE_TOKEN }}
run: >-
python -m pytest -x
-m config -c /dev/null -p no:warnings
-n 0 -v -o cache_dir=${GITHUB_WORKSPACE}/.pytest-cache
executor_tests:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.13
uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install dependencies
run: |
python -m pip install poetry
poetry self add poetry-plugin-export
poetry export -f requirements.txt --with dev -o requirements.txt --all-extras
python -m pip install -r requirements.txt
python -m pip install .
- name: Test with pytest
env:
SIMVUE_URL: ${{ secrets.SIMVUE_URL }}
SIMVUE_TOKEN: ${{ secrets.SIMVUE_TOKEN }}
run: >-
python -m pytest -x
-m executor -c /dev/null -p no:warnings
-n 0 -v -o cache_dir=${GITHUB_WORKSPACE}/.pytest-cache
api_tests:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.13
uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install dependencies
run: |
python -m pip install poetry
poetry self add poetry-plugin-export
poetry export -f requirements.txt --with dev -o requirements.txt --all-extras
python -m pip install -r requirements.txt
python -m pip install .
- name: Test with pytest
env:
SIMVUE_URL: ${{ secrets.SIMVUE_URL }}
SIMVUE_TOKEN: ${{ secrets.SIMVUE_TOKEN }}
run: >-
python -m pytest -x
-m api -c /dev/null -p no:warnings
-n 0 -v -o cache_dir=${GITHUB_WORKSPACE}/.pytest-cache
local_tests:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.13
Expand All @@ -158,15 +228,13 @@ jobs:
python -m pip install poetry
poetry self add poetry-plugin-export
poetry export -f requirements.txt --with dev -o requirements.txt --all-extras
python -m pip install torch --index-url https://download.pytorch.org/whl/cpu
python -m pip install -r requirements.txt
python -m pip install .
- name: Test with pytest
env:
SIMVUE_URL: ${{ secrets.SIMVUE_URL }}
SIMVUE_TOKEN: ${{ secrets.SIMVUE_TOKEN }}
run: >-
python -m pytest tests/functional/ -x
-m 'not offline' -m 'not online'
-m 'not scenario' -c /dev/null
-p no:warnings -n 0 -v -o cache_dir=${GITHUB_WORKSPACE}/.pytest-cache
python -m pytest -x
-m local -c /dev/null -p no:warnings
-n 0 -v -o cache_dir=${GITHUB_WORKSPACE}/.pytest-cache
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
* Fixed bug in pagination whereby the count value specified by the user is ignored.
* Fixed bug where uploading larger files timed out leading to file of size 0B.
* Fixed bug where if the range or threshold of an alert is zero the alert type validation fails.
* Fixed bug in `Folder.ids` where `kwargs` were not being passed to `GET`.
* Ensured all threads have `daemon=True` to prevent hanging on termination.
* Added error when `close()` method is called within the `simvue.Run` context manager.
## [v2.1.1](https://github.com/simvue-io/client/releases/tag/v2.1.1) - 2025-04-25
* Changed from CO2 Signal to ElectricityMaps
* Fixed a number of bugs in how offline mode is handled with emissions
Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ testpaths = [
markers = [
"eco: tests for emission metrics",
"client: tests of Simvue client",
"converters: tests for Simvue object converters",
"dispatch: test data dispatcher",
"run: test the simvue Run class",
"utilities: test simvue utilities module",
Expand All @@ -107,10 +106,11 @@ markers = [
"api: tests of RestAPI functionality",
"unix: tests for UNIX systems only",
"metadata: tests of metadata gathering functions",
"proxies: tests for remote/offline Simvue proxies",
"online: tests for online functionality",
"offline: tests for offline functionality",
"local: tests of functionality which do not involve a server or writing to an offline cache file"
"local: tests of functionality which do not involve a server or writing to an offline cache file",
"object_retrieval: tests relating to retrieval of objects from the server",
"object_removal: tests relating to removal of objects from the server",
]

[tool.interrogate]
Expand Down
28 changes: 24 additions & 4 deletions simvue/api/objects/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ def ids(
"""
_class_instance = cls(_read_only=True, _local=True)
_count: int = 0
for response in cls._get_all_objects(offset, count=count):
for response in cls._get_all_objects(offset, count=count, **kwargs):
if (_data := response.get("data")) is None:
raise RuntimeError(
f"Expected key 'data' for retrieval of {_class_instance.__class__.__name__.lower()}s"
Expand Down Expand Up @@ -412,6 +412,10 @@ def get(
f"Expected key 'data' for retrieval of {_class_instance.__class__.__name__.lower()}s"
)

# If data is an empty list
if not _data:
return

for entry in _data:
_id = entry["id"]
yield _id, cls(_read_only=True, identifier=_id, _local=True, **entry)
Expand Down Expand Up @@ -473,7 +477,7 @@ def read_only(self, is_read_only: bool) -> None:
if not self._read_only:
self._staging = self._get_local_staged()

def commit(self) -> None:
def commit(self) -> dict | None:
"""Send updates to the server, or if offline, store locally."""
if self._read_only:
raise AttributeError("Cannot commit object in 'read-only' mode")
Expand All @@ -485,22 +489,26 @@ def commit(self) -> None:
self._cache()
return

_response: dict | None = None

# Initial commit is creation of object
# if staging is empty then we do not need to use PUT
if not self._identifier or self._identifier.startswith("offline_"):
self._logger.debug(
f"Posting from staged data for {self._label} '{self.id}': {self._staging}"
)
self._post(**self._staging)
_response = self._post(**self._staging)
elif self._staging:
self._logger.debug(
f"Pushing updates from staged data for {self._label} '{self.id}': {self._staging}"
)
self._put(**self._staging)
_response = self._put(**self._staging)

# Clear staged changes
self._clear_staging()

return _response

@property
def id(self) -> str | None:
"""The identifier for this object if applicable.
Expand Down Expand Up @@ -686,3 +694,15 @@ def staged(self) -> dict[str, typing.Any] | None:
the locally staged data if available.
"""
return self._staging or None

def __str__(self) -> str:
"""String representation of Simvue object."""
return f"{self.__class__.__name__}({self.id=})"

def __repr__(self) -> str:
_out_str = f"{self.__class__.__module__}.{self.__class__.__qualname__}("
_out_str += ", ".join(
f"{property}={getattr(self, property)!r}" for property in self._properties
)
_out_str += ")"
return _out_str
5 changes: 5 additions & 0 deletions simvue/api/url.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ def __truediv__(self, other: str) -> Self:
_new /= other
return _new

def __repr__(self) -> str:
"""Representation of URL"""
_out_str = f"{self.__class__.__module__}.{self.__class__.__qualname__}"
return f"{_out_str}(url={self.__str__()!r})"

@pydantic.validate_call
def __itruediv__(self, other: str) -> Self:
"""Define URL extension through use of '/'"""
Expand Down
Loading