Skip to content
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

Create versioned Compute clients #1096

Merged
merged 3 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Added
~~~~~

- Created ``ComputeClientV2`` and ``ComputeClientV3`` classes to support Globus Compute
API versions 2 and 3, respectively. The canonical ``ComputeClient`` is now a subclass
of ``ComputeClientV2``, preserving backward compatibility. (:pr:`NUMBER`)
30 changes: 30 additions & 0 deletions docs/services/compute.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,47 @@ Globus Compute

.. currentmodule:: globus_sdk

The standard way to interact with the Globus Compute service is through the
`Globus Compute SDK <https://globus-compute.readthedocs.io/en/stable/sdk.html>`_,
a separate, specialized toolkit that offers enhanced functionality for Globus Compute.
Under the hood, the Globus Compute SDK uses the following clients to interact with
the Globus Compute API. Advanced users may choose to work directly with these clients
for custom implementations.

The canonical :class:`ComputeClient` is a subclass of :class:`ComputeClientV2`,
which supports version 2 of the Globus Compute API. When feasible, new projects
should use :class:`ComputeClientV3`, which supports version 3 and includes the
latest API features and improvements.

.. autoclass:: ComputeClient
:members:
:member-order: bysource
:show-inheritance:
:exclude-members: error_class, scopes

.. autoclass:: ComputeClientV2
:members:
:member-order: bysource
:show-inheritance:
:exclude-members: error_class, scopes

.. attribute:: scopes

.. listknownscopes:: globus_sdk.scopes.ComputeScopes
:base_name: ComputeClient.scopes

.. autoclass:: ComputeClientV3
:members:
:member-order: bysource
:show-inheritance:
:exclude-members: error_class, scopes

rjmello marked this conversation as resolved.
Show resolved Hide resolved
.. attribute:: scopes

.. listknownscopes:: globus_sdk.scopes.ComputeScopes
:base_name: ComputeClient.scopes


sirosen marked this conversation as resolved.
Show resolved Hide resolved
Client Errors
-------------

Expand Down
6 changes: 6 additions & 0 deletions src/globus_sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ def _force_eager_imports() -> None:
},
"services.compute": {
"ComputeClient",
"ComputeClientV2",
"ComputeClientV3",
"ComputeAPIError",
"ComputeFunctionDocument",
"ComputeFunctionMetadata",
Expand Down Expand Up @@ -206,6 +208,8 @@ def _force_eager_imports() -> None:
from .services.auth import OAuthTokenResponse
from .services.auth import DependentScopeSpec
from .services.compute import ComputeClient
from .services.compute import ComputeClientV2
from .services.compute import ComputeClientV3
from .services.compute import ComputeAPIError
from .services.compute import ComputeFunctionDocument
from .services.compute import ComputeFunctionMetadata
Expand Down Expand Up @@ -333,6 +337,8 @@ def __getattr__(name: str) -> t.Any:
"CollectionPolicies",
"ComputeAPIError",
"ComputeClient",
"ComputeClientV2",
"ComputeClientV3",
"ComputeFunctionDocument",
"ComputeFunctionMetadata",
"ConfidentialAppAuthClient",
Expand Down
2 changes: 2 additions & 0 deletions src/globus_sdk/_generate_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ def __getattr__(name: str) -> t.Any:
"services.compute",
(
"ComputeClient",
"ComputeClientV2",
"ComputeClientV3",
"ComputeAPIError",
"ComputeFunctionDocument",
"ComputeFunctionMetadata",
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from globus_sdk._testing.models import RegisteredResponse, ResponseSet

from ._common import FUNCTION_ID
from .._common import FUNCTION_ID
sirosen marked this conversation as resolved.
Show resolved Hide resolved

RESPONSES = ResponseSet(
metadata={"function_id": FUNCTION_ID},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from globus_sdk._testing.models import RegisteredResponse, ResponseSet

from ._common import FUNCTION_CODE, FUNCTION_ID, FUNCTION_NAME
from .._common import FUNCTION_CODE, FUNCTION_ID, FUNCTION_NAME

FUNCTION_DOC = {
"function_uuid": FUNCTION_ID,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from globus_sdk._testing.models import RegisteredResponse, ResponseSet

from ._common import FUNCTION_CODE, FUNCTION_ID, FUNCTION_NAME
from .._common import FUNCTION_CODE, FUNCTION_ID, FUNCTION_NAME

RESPONSES = ResponseSet(
metadata={
Expand Down
15 changes: 15 additions & 0 deletions src/globus_sdk/_testing/registry.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import importlib
import re
import typing as t

import responses
Expand All @@ -9,6 +10,10 @@

from .models import RegisteredResponse, ResponseList, ResponseSet

# matches "V2", "V11", etc as a string suffix
# see usage in _resolve_qualname for details
_SUFFIX_VERSION_MATCH_PATTERN = re.compile(r"V\d+$")

_RESPONSE_SET_REGISTRY: dict[t.Any, ResponseSet] = {}


Expand Down Expand Up @@ -53,6 +58,16 @@ def _resolve_qualname(name: str) -> str:

assert issubclass(maybe_client, globus_sdk.BaseClient)
service_name = maybe_client.service_name

# TODO: Consider alternative strategies for mapping versioned clients
# to subdirs. For now, we do it by name matching.
sirosen marked this conversation as resolved.
Show resolved Hide resolved
#
# 'prefix' is the client name, and it may end in `V2`, `V3`, etc.
# in which case we want to map it to a subdir
suffix_version_match = _SUFFIX_VERSION_MATCH_PATTERN.search(prefix)
if suffix_version_match:
suffix = f"{suffix_version_match.group(0).lower()}.{suffix}"

return f"{service_name}.{suffix}"


Expand Down
4 changes: 3 additions & 1 deletion src/globus_sdk/services/compute/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from .client import ComputeClient
from .client import ComputeClient, ComputeClientV2, ComputeClientV3
from .data import ComputeFunctionDocument, ComputeFunctionMetadata
from .errors import ComputeAPIError

__all__ = (
"ComputeAPIError",
"ComputeClient",
"ComputeClientV2",
"ComputeClientV3",
"ComputeFunctionDocument",
"ComputeFunctionMetadata",
)
28 changes: 25 additions & 3 deletions src/globus_sdk/services/compute/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
log = logging.getLogger(__name__)


class ComputeClient(client.BaseClient):
class ComputeClientV2(client.BaseClient):
r"""
Client for the Globus Compute API.
Client for the Globus Compute API, version 2.

.. automethodlist:: globus_sdk.ComputeClient
.. automethodlist:: globus_sdk.ComputeClientV2
"""

error_class = ComputeAPIError
Expand Down Expand Up @@ -71,3 +71,25 @@ def delete_function(self, function_id: UUIDLike) -> GlobusHTTPResponse:
:ref: Functions/operation/delete_function_v2_functions__function_uuid__delete
""" # noqa: E501
return self.delete(f"/v2/functions/{function_id}")


class ComputeClientV3(client.BaseClient):
r"""
Client for the Globus Compute API, version 3.

.. automethodlist:: globus_sdk.ComputeClientV3
"""

error_class = ComputeAPIError
service_name = "compute"
scopes = ComputeScopes
default_scope_requirements = [Scope(ComputeScopes.all)]


class ComputeClient(ComputeClientV2):
r"""
Canonical client for the Globus Compute API, with support exclusively for
API version 2.

.. automethodlist:: globus_sdk.ComputeClient
"""
6 changes: 3 additions & 3 deletions tests/functional/services/compute/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@


@pytest.fixture
def compute_client(no_retry_transport):
class CustomComputeClient(globus_sdk.ComputeClient):
def compute_client_v2(no_retry_transport):
class CustomComputeClientV2(globus_sdk.ComputeClientV2):
transport_class = no_retry_transport

return CustomComputeClient()
return CustomComputeClientV2()
9 changes: 0 additions & 9 deletions tests/functional/services/compute/test_delete_function.py

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import globus_sdk
from globus_sdk._testing import load_response


def test_delete_function(compute_client_v2: globus_sdk.ComputeClientV2):
meta = load_response(compute_client_v2.delete_function).metadata
res = compute_client_v2.delete_function(function_id=meta["function_id"])
assert res.http_status == 200
assert res.data == {"result": 302}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
from globus_sdk._testing import load_response


def test_get_function(compute_client: globus_sdk.ComputeClient):
meta = load_response(compute_client.get_function).metadata
res = compute_client.get_function(function_id=meta["function_id"])
def test_get_function(compute_client_v2: globus_sdk.ComputeClientV2):
meta = load_response(compute_client_v2.get_function).metadata
res = compute_client_v2.get_function(function_id=meta["function_id"])
assert res.http_status == 200
assert res.data["function_uuid"] == meta["function_id"]
assert res.data["function_name"] == meta["function_name"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
from globus_sdk._testing import load_response


def test_register_function(compute_client: globus_sdk.ComputeClient):
meta = load_response(compute_client.register_function).metadata
def test_register_function(compute_client_v2: globus_sdk.ComputeClientV2):
meta = load_response(compute_client_v2.register_function).metadata
registration_doc = {
"function_name": meta["function_name"],
"function_code": meta["function_code"],
}
res = compute_client.register_function(function_data=registration_doc)
res = compute_client_v2.register_function(function_data=registration_doc)
assert res.http_status == 200
assert res.data["function_uuid"] == meta["function_id"]
6 changes: 6 additions & 0 deletions tests/unit/services/compute/test_canononical_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from globus_sdk.services.compute.client import ComputeClient, ComputeClientV2


def test_canonical_client_is_v2():
client = ComputeClient()
assert isinstance(client, ComputeClientV2)