Skip to content

Commit

Permalink
feat: add version check support
Browse files Browse the repository at this point in the history
  • Loading branch information
tdstein committed Sep 25, 2024
1 parent 0614ae4 commit 3048652
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 9 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ lint:
$(PYTHON) -m ruff check

test:
$(PYTHON) -m coverage run --source=src -m pytest tests
$(PYTHON) -m coverage run --source=src -m pytest -s tests

uninstall: ensure-uv
$(UV) pip uninstall $(PROJECT_NAME)
Expand Down
2 changes: 1 addition & 1 deletion integration/tests/posit/connect/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
from posit import connect

client = connect.Client()
CONNECT_VERSION = version.parse(client.version)
CONNECT_VERSION = version.parse(client.version) if client.version else version.parse("0.0.0")
10 changes: 7 additions & 3 deletions src/posit/connect/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

from typing import overload
from typing import Optional, overload

from requests import Response, Session

Expand All @@ -14,8 +14,10 @@
from .metrics import Metrics
from .oauth import OAuth
from .resources import ResourceParameters
from .settings import Settings
from .tasks import Tasks
from .users import User, Users
from .version import requires_version


class Client:
Expand Down Expand Up @@ -158,7 +160,7 @@ def __init__(self, *args, **kwargs) -> None:
self.resource_params = ResourceParameters(session, self.cfg.url)

@property
def version(self) -> str:
def version(self) -> Optional[str]:
"""
The server version.
Expand All @@ -167,7 +169,8 @@ def version(self) -> str:
str
The version of the Posit Connect server.
"""
return self.get("server_settings").json()["version"]
settings = Settings(self.session, self.cfg.url)
return settings.version

@property
def me(self) -> User:
Expand Down Expand Up @@ -257,6 +260,7 @@ def metrics(self) -> Metrics:
return Metrics(self.resource_params)

@property
@requires_version("2024.08.0")
def oauth(self) -> OAuth:
"""
The OAuth API interface.
Expand Down
14 changes: 13 additions & 1 deletion src/posit/connect/errors.py → src/posit/connect/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
from typing import Any


class ClientError(Exception):
class PositConnectException(RuntimeError):
pass


class ClientError(PositConnectException):
def __init__(
self,
error_code: int,
Expand All @@ -27,3 +31,11 @@ def __init__(
}
)
)


class VersionUpgradeRequiredException(PositConnectException):
def __init__(self, found: str, expected: str, *args) -> None:
super().__init__(
f"This API is not available in Connect version {found}. Please upgrade to version {expected} or later.",
*args,
)
2 changes: 1 addition & 1 deletion src/posit/connect/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from requests import JSONDecodeError, Response

from .errors import ClientError
from .exceptions import ClientError


def handle_errors(response: Response, *args, **kwargs) -> Response:
Expand Down
30 changes: 30 additions & 0 deletions src/posit/connect/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from typing import Optional

import requests

from .urls import Url


class Settings:
def __init__(self, session: requests.Session, url: Url) -> None:
self.session = session
self.url = url

@property
def version(self) -> Optional[str]:
return self.settings.get("version")

@property
def settings(self) -> dict:
url = self.url + "server_settings"
try:
response = self.session.get(url)
return response.json()
except requests.exceptions.RequestException as e:
import logging

logging.debug(
f"Failed to retrieve server settings from {url}. Error: {str(e)}", exc_info=True
)

return {}
61 changes: 61 additions & 0 deletions src/posit/connect/version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from functools import wraps

from packaging.version import Version

from .exceptions import VersionUpgradeRequiredException


def requires_version(expected: str):
"""
Decorator to enforce a minimum version requirement for a method.
This decorator checks the `version` attribute of the class instance and raises a `VersionUpgradeRequiredException` if the version is lower than the specified `expected` version.
Parameters
----------
expected : str
The minimum version required for the decorated method to execute. It is compared against the class instance's `version` attribute.
Returns
-------
function
The wrapped function that enforces the version check.
Raises
------
VersionUpgradeRequiredException
If the version specified in the class instance's `version` attribute is lower than the `expected` version.
Examples
--------
To use this decorator, apply it to any method that requires a minimum version:
>>> class Client:
>>> version = "2024.07.0"
>>>
>>> @requires_version("2024.08.0")
>>> def some_method(self):
>>> pass
>>>
>>> client = Client()
>>> client.some_method()
Traceback (most recent call last):
...
VersionUpgradeRequiredException: This API is not available in Connect version 2024.07.0. Please upgrade to version 2024.08.0 or later."
"""

def decorator(func):
@wraps(func)
def wrapper(self, *args, **kwargs):
if hasattr(self, "version"):
version = getattr(self, "version")
if not version:
return
if Version(version) < Version(expected):
raise VersionUpgradeRequiredException(version, expected)
return func(self, *args, **kwargs)

return wrapper

return decorator
2 changes: 1 addition & 1 deletion tests/posit/connect/test_errors.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest

from posit.connect.errors import ClientError
from posit.connect.exceptions import ClientError


def test():
Expand Down
2 changes: 1 addition & 1 deletion tests/posit/connect/test_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from requests import HTTPError, Response

from posit.connect import Client
from posit.connect.errors import ClientError
from posit.connect.exceptions import ClientError
from posit.connect.hooks import handle_errors


Expand Down

0 comments on commit 3048652

Please sign in to comment.