From c0069c2e14660768969ed5a056bfae90c365fd7a Mon Sep 17 00:00:00 2001 From: minottic Date: Thu, 11 Dec 2025 12:21:17 +0100 Subject: [PATCH] Selectively return scicat v3 or v4 class --- sdk/python/scilog/scicat.py | 22 +++++- sdk/python/tests/test_scicat.py | 93 ++++---------------------- sdk/python/tests/test_scicat_legacy.py | 47 +++++++++++++ sdk/python/tests/test_scicat_new.py | 87 ++++++++++++++++++++++++ 4 files changed, 166 insertions(+), 83 deletions(-) create mode 100644 sdk/python/tests/test_scicat_legacy.py create mode 100644 sdk/python/tests/test_scicat_new.py diff --git a/sdk/python/scilog/scicat.py b/sdk/python/scilog/scicat.py index 8a9e026e..4a230935 100644 --- a/sdk/python/scilog/scicat.py +++ b/sdk/python/scilog/scicat.py @@ -22,7 +22,17 @@ def authenticate(self, username, password): return token -class SciCat: +class SciCatLegacy: + def __init__(self, *args, **kwargs): + self.http_client = SciCatRestAPI(*args, **kwargs) + + @property + def proposals(self): + url = self.http_client.address + "/proposals" + return self.http_client.get_request(url, headers=HEADER_JSON) + + +class SciCatNew: max_iterations = 1000 def __init__(self, *args, return_options=None, **kwargs): @@ -39,7 +49,7 @@ def _proposals_batch(self): if iteration > self.max_iterations: raise RuntimeError("Exceeded maximum iterations in proposals_batch") proposals = self.http_client.get_request( - f"{url}?filter={quote(dumps(filter))}", headers=HEADER_JSON + f"{url}?filters={quote(dumps(filter))}", headers=HEADER_JSON ) if not proposals or len(proposals) == 0: break @@ -53,5 +63,13 @@ def proposals(self): return generator if lazy else list(generator) +class SciCat: + def __new__(cls, *args, **kwargs): + token_prefix = kwargs.get("options", {}).get("token_prefix") + if token_prefix: + return SciCatNew(*args, **kwargs) + return SciCatLegacy(*args, **kwargs) + + class SciCatAuthError(AuthError): pass diff --git a/sdk/python/tests/test_scicat.py b/sdk/python/tests/test_scicat.py index 34f4f4c0..61859168 100644 --- a/sdk/python/tests/test_scicat.py +++ b/sdk/python/tests/test_scicat.py @@ -1,92 +1,23 @@ -from json import dumps -from unittest.mock import ANY, Mock, call, patch -from urllib.parse import quote +from unittest.mock import ANY, Mock, patch import pytest from scilog import SciCat -ADDRESS = "http://scicat" - -@patch("requests.post") -@patch("requests.get") @pytest.mark.parametrize( - "token_prefix", + "token_prefix, expected_class", [ - "", - None, - "Bearer ", + ["", "SciCatLegacy"], + [None, "SciCatLegacy"], + ["Bearer ", "SciCatNew"], ], ) -def test_get_proposals(mock_get, mock_post, token_prefix): - options = { - "username": f"username{token_prefix}", - "password": "password", - "login_path": f"{ADDRESS}/login", - "token_prefix": token_prefix, - } - headers = {"Content-type": "application/json", "Accept": "application/json"} - token = "token123" - - scicat = SciCat(ADDRESS, options=options) - scicat.http_client.config = {} - mock_response = Mock() - mock_response.json.return_value = {"id": token} - mock_post.return_value = mock_response - list(scicat.proposals) - mock_post.assert_called_with( - options["login_path"], - json={"username": options["username"], "password": options["password"]}, - headers=headers, - timeout=ANY, - verify=True, +def test_new(token_prefix, expected_class): + scicat = SciCat( + "http://scicat", + options={ + "token_prefix": token_prefix, + }, ) - filter = {"limits": {"skip": 0, "limit": 500}} - mock_get.assert_called_with( - f"{ADDRESS}/proposals?filter={quote(dumps(filter))}", - params=None, - headers={**headers, "Authorization": f"{token_prefix or ''}{token}"}, - timeout=ANY, - verify=True, - ) - - -@patch("scilog.scicat.SciCatRestAPI.get_request") -def test__proposals_batch(mock_get): - scicat = SciCat(ADDRESS) - scicat.http_client.config = {} - - mock_get.side_effect = [[1, 2], [3, 4], []] - filters = [{"limits": {"skip": 0, "limit": 500}}, {"limits": {"skip": 500, "limit": 500}}] - for _ in scicat._proposals_batch(): - continue - - assert mock_get.call_count == 3 - - expected_calls = [ - call(f"{ADDRESS}/proposals?filter={quote(dumps(filters[0]))}", headers=ANY), - call(f"{ADDRESS}/proposals?filter={quote(dumps(filters[1]))}", headers=ANY), - ] - mock_get.assert_has_calls(expected_calls, any_order=False) - - -@patch("scilog.scicat.SciCatRestAPI.get_request") -@pytest.mark.parametrize( - "return_options", - [ - None, - {"lazy": True}, - {"lazy": False}, - ], -) -def test_proposals(mock_get, return_options): - scicat = SciCat(ADDRESS, return_options=return_options) - scicat.http_client.config = {} - mock_get.side_effect = [[1, 2], [3, 4], []] - proposals = [1, 2, 3, 4] - scicat_proposals = scicat.proposals - for i, p in enumerate(scicat_proposals): - assert p == proposals[i] - lazy = return_options.get("lazy", False) if return_options else False - assert len(list(scicat_proposals)) == (0 if lazy else len(proposals)) + assert scicat.__class__.__name__ == expected_class diff --git a/sdk/python/tests/test_scicat_legacy.py b/sdk/python/tests/test_scicat_legacy.py new file mode 100644 index 00000000..3dadb0c7 --- /dev/null +++ b/sdk/python/tests/test_scicat_legacy.py @@ -0,0 +1,47 @@ +from unittest.mock import ANY, Mock, patch + +import pytest + +from scilog import SciCat + + +@patch("requests.post") +@patch("requests.get") +@pytest.mark.parametrize( + "token_prefix", + [ + "", + None, + ], +) +def test_get_proposals(mock_get, mock_post, token_prefix): + address = "http://scicat" + options = { + "username": f"username{token_prefix}", + "password": "password", + "login_path": f"{address}/login", + "token_prefix": token_prefix, + } + headers = {"Content-type": "application/json", "Accept": "application/json"} + token = "token123" + + scicat = SciCat(address, options=options) + mock_response = Mock() + mock_response.json.return_value = {"id": token} + mock_post.return_value = mock_response + scicat.http_client.config = {} + scicat.proposals + mock_post.assert_called_with( + options["login_path"], + json={"username": options["username"], "password": options["password"]}, + headers=headers, + timeout=ANY, + verify=True, + ) + mock_get.assert_called_with( + f"{address}/proposals", + params=None, + headers={**headers, "Authorization": f"{token_prefix or ''}{token}"}, + timeout=ANY, + verify=True, + ) diff --git a/sdk/python/tests/test_scicat_new.py b/sdk/python/tests/test_scicat_new.py new file mode 100644 index 00000000..6ed78d7c --- /dev/null +++ b/sdk/python/tests/test_scicat_new.py @@ -0,0 +1,87 @@ +from json import dumps +from unittest.mock import ANY, Mock, call, patch +from urllib.parse import quote + +import pytest + +from scilog import SciCat + +ADDRESS = "http://scicat" + +OPTIONS = { + "username": f"username", + "password": "password", + "login_path": f"{ADDRESS}/login", + "token_prefix": "Bearer ", +} + + +@pytest.fixture() +def scicat(): + scicat = SciCat(ADDRESS, options=OPTIONS) + scicat.http_client.config = {} + return scicat + + +@patch("requests.post") +@patch("requests.get") +def test_get_proposals(mock_get, mock_post, scicat): + headers = {"Content-type": "application/json", "Accept": "application/json"} + token = "token123" + mock_response = Mock() + mock_response.json.return_value = {"id": token} + mock_post.return_value = mock_response + list(scicat.proposals) + mock_post.assert_called_with( + OPTIONS["login_path"], + json={"username": OPTIONS["username"], "password": OPTIONS["password"]}, + headers=headers, + timeout=ANY, + verify=True, + ) + filter = {"limits": {"skip": 0, "limit": 500}} + mock_get.assert_called_with( + f"{ADDRESS}/proposals?filters={quote(dumps(filter))}", + params=None, + headers={**headers, "Authorization": f"{OPTIONS['token_prefix']}{token}"}, + timeout=ANY, + verify=True, + ) + + +@patch("scilog.scicat.SciCatRestAPI.get_request") +def test__proposals_batch(mock_get, scicat): + + mock_get.side_effect = [[1, 2], [3, 4], []] + filters = [{"limits": {"skip": 0, "limit": 500}}, {"limits": {"skip": 500, "limit": 500}}] + for _ in scicat._proposals_batch(): + continue + + assert mock_get.call_count == 3 + + expected_calls = [ + call(f"{ADDRESS}/proposals?filters={quote(dumps(filters[0]))}", headers=ANY), + call(f"{ADDRESS}/proposals?filters={quote(dumps(filters[1]))}", headers=ANY), + ] + mock_get.assert_has_calls(expected_calls, any_order=False) + + +@patch("scilog.scicat.SciCatRestAPI.get_request") +@pytest.mark.parametrize( + "return_options", + [ + None, + {"lazy": True}, + {"lazy": False}, + ], +) +def test_proposals(mock_get, return_options): + scicat = SciCat(ADDRESS, options=OPTIONS, return_options=return_options) + scicat.http_client.config = {} + mock_get.side_effect = [[1, 2], [3, 4], []] + proposals = [1, 2, 3, 4] + scicat_proposals = scicat.proposals + for i, p in enumerate(scicat_proposals): + assert p == proposals[i] + lazy = return_options.get("lazy", False) if return_options else False + assert len(list(scicat_proposals)) == (0 if lazy else len(proposals))