Skip to content

Commit

Permalink
Merge branch 'main' into add-rows
Browse files Browse the repository at this point in the history
  • Loading branch information
sg-s authored Nov 21, 2024
2 parents 396e0a6 + fb3e218 commit 4f7e2ff
Show file tree
Hide file tree
Showing 12 changed files with 214 additions and 3 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ install:
@python3 -m venv venv
@source $(CURDIR)/venv/bin/activate && \
pip install --upgrade pip && \
pip install -e .[lint,test,dev,docs,plots] && \
pip install --no-cache-dir -e .[lint,test,dev,docs,plots] && \
deactivate
@-mkdir -p ~/.deeporigin
@test -f ~/.deeporigin/deeporigin || ln -s $(CURDIR)/venv/bin/deeporigin ~/.deeporigin/deeporigin
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ dependencies = [
"pyjwt",
"cryptography",
"python-box",
"do-sdk-platform==1.0.0",
]
dynamic = ["version"]

Expand Down
4 changes: 3 additions & 1 deletion src/data_hub/_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import sys
from pathlib import Path

from beartype import beartype
from box import Box
from deeporigin import auth
from deeporigin.exceptions import DeepOriginException
Expand Down Expand Up @@ -47,7 +48,8 @@
}


def _get_client_methods():
@beartype
def _get_client_methods() -> set:
# the only reason we're creating this client is to
# extract methods from it. So no need to
# authenticate
Expand Down
Empty file added src/platform/__init__.py
Empty file.
12 changes: 12 additions & 0 deletions src/platform/organizations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""bridge module to interact with the platform organizations api"""

import sys

from deeporigin.platform.utils import add_functions_to_module

methods = add_functions_to_module(
module=sys.modules[__name__],
api_name="OrganizationsApi",
)

__all__ = list(methods)
12 changes: 12 additions & 0 deletions src/platform/tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""bridge module to interact with the platform tools api"""

import sys

from deeporigin.platform.utils import add_functions_to_module

methods = add_functions_to_module(
module=sys.modules[__name__],
api_name="ToolsApi",
)

__all__ = list(methods)
12 changes: 12 additions & 0 deletions src/platform/users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""bridge module to interact with the platform users api"""

import sys

from deeporigin.platform.utils import add_functions_to_module

methods = add_functions_to_module(
module=sys.modules[__name__],
api_name="UsersApi",
)

__all__ = list(methods)
146 changes: 146 additions & 0 deletions src/platform/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
"""module to automatically wrap methods in autogenerated low-level code and re-expose them as high-level functions"""

import inspect
from urllib.parse import urljoin

import do_sdk_platform
from beartype import beartype
from box import Box
from deeporigin.auth import get_tokens
from deeporigin.config import get_value
from deeporigin.utils.core import _get_method


@beartype
def add_functions_to_module(
module: str,
api_name: str,
) -> set:
"""utility function to dynamically add functions to a module
This function works by calling setattr on the module.
Args:
module (str): name of the module
api_name (str): name of the API
Returns:
set of methods that were added
"""
methods = _get_client_methods(
_get_api_client(
api_name=api_name,
)
)

for method in methods:
# clean up the name so that it's more readable
sanitized_method_name = method.split("controller")[0].rstrip("_")

# add this function as an attribute to this module
# so that we can call it
setattr(
module,
sanitized_method_name,
_create_function(
method_path=method,
api_name=api_name,
),
)

return methods


@beartype
def _get_api_client(*, api_name: str, configure: bool = True):
"""return a configured client for the API we want to access
Args:
api_name (str): name of the API
Returns:
configured client
"""

if configure:
configuration = do_sdk_platform.configuration.Configuration(
host=urljoin(get_value()["api_endpoint"], "/api"),
access_token=get_tokens()["access"],
)

client = do_sdk_platform.ApiClient(configuration=configuration)
else:
client = do_sdk_platform.ApiClient()

api_class = getattr(do_sdk_platform, api_name)
client = api_class(api_client=client)
return client


@beartype
def _get_client_methods(client) -> set:
"""utility function to get methods from the client that return raw responses from the server"""
methods = set(
[
attr
for attr in dir(client)
if callable(getattr(client, attr))
and not attr.startswith("_")
and "without_preload_content" in attr
]
)

return methods


def _create_function(*, method_path: str, api_name: str):
"""utility function the dynamically creates functions
that wrap low-level functions in the DeepOrigin data API"""

# we're constructing a client solely for the purposes
# of inspecting its methods and extracting
# function signatures. So we don't need any
# authentication

client = _get_api_client(
configure=False,
api_name=api_name,
)

method = _get_method(client, method_path)

signature = inspect.signature(method)

def dynamic_function(
*,
client=None,
**kwargs,
):
"""dynamic function that wraps low-level functions in the DeepOrigin platform API"""

if client is None:
client = _get_api_client(api_name=api_name)
method = _get_method(client, method_path)

# call the low level API method
response = method(**kwargs)

if not isinstance(response, dict):
response = response.json()

if "data" in response.keys():
response = response["data"]
if isinstance(response, list):
response = [Box(item) for item in response]
else:
response = Box(response)
else:
response = Box(response)

return response

# attach the signature of the underlying method to the
# function so that IDEs can display it properly
dynamic_function.__signature__ = signature

return dynamic_function
12 changes: 12 additions & 0 deletions src/platform/volumes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""bridge module to interact with the platform tools api"""

import sys

from deeporigin.platform.utils import add_functions_to_module

methods = add_functions_to_module(
module=sys.modules[__name__],
api_name="VolumesApi",
)

__all__ = list(methods)
12 changes: 12 additions & 0 deletions src/platform/workstations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""bridge module to interact with the platform tools api"""

import sys

from deeporigin.platform.utils import add_functions_to_module

methods = add_functions_to_module(
module=sys.modules[__name__],
api_name="ComputebenchesApi",
)

__all__ = list(methods)
2 changes: 1 addition & 1 deletion src/utils/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def clear(self):
self._save(self._data)


def _get_method(obj, method_path):
def _get_method(obj, method_path: str):
# Split the method path into components
methods = method_path.split(".")

Expand Down
2 changes: 2 additions & 0 deletions tests/utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""helper module to set up tests"""

import io
from contextlib import redirect_stderr, redirect_stdout

Expand Down

0 comments on commit 4f7e2ff

Please sign in to comment.