Skip to content

Commit 3048652

Browse files
committed
feat: add version check support
1 parent 0614ae4 commit 3048652

File tree

9 files changed

+116
-9
lines changed

9 files changed

+116
-9
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ lint:
5858
$(PYTHON) -m ruff check
5959

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

6363
uninstall: ensure-uv
6464
$(UV) pip uninstall $(PROJECT_NAME)

integration/tests/posit/connect/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
from posit import connect
44

55
client = connect.Client()
6-
CONNECT_VERSION = version.parse(client.version)
6+
CONNECT_VERSION = version.parse(client.version) if client.version else version.parse("0.0.0")

src/posit/connect/client.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from __future__ import annotations
44

5-
from typing import overload
5+
from typing import Optional, overload
66

77
from requests import Response, Session
88

@@ -14,8 +14,10 @@
1414
from .metrics import Metrics
1515
from .oauth import OAuth
1616
from .resources import ResourceParameters
17+
from .settings import Settings
1718
from .tasks import Tasks
1819
from .users import User, Users
20+
from .version import requires_version
1921

2022

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

160162
@property
161-
def version(self) -> str:
163+
def version(self) -> Optional[str]:
162164
"""
163165
The server version.
164166
@@ -167,7 +169,8 @@ def version(self) -> str:
167169
str
168170
The version of the Posit Connect server.
169171
"""
170-
return self.get("server_settings").json()["version"]
172+
settings = Settings(self.session, self.cfg.url)
173+
return settings.version
171174

172175
@property
173176
def me(self) -> User:
@@ -257,6 +260,7 @@ def metrics(self) -> Metrics:
257260
return Metrics(self.resource_params)
258261

259262
@property
263+
@requires_version("2024.08.0")
260264
def oauth(self) -> OAuth:
261265
"""
262266
The OAuth API interface.

src/posit/connect/errors.py renamed to src/posit/connect/exceptions.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
from typing import Any
33

44

5-
class ClientError(Exception):
5+
class PositConnectException(RuntimeError):
6+
pass
7+
8+
9+
class ClientError(PositConnectException):
610
def __init__(
711
self,
812
error_code: int,
@@ -27,3 +31,11 @@ def __init__(
2731
}
2832
)
2933
)
34+
35+
36+
class VersionUpgradeRequiredException(PositConnectException):
37+
def __init__(self, found: str, expected: str, *args) -> None:
38+
super().__init__(
39+
f"This API is not available in Connect version {found}. Please upgrade to version {expected} or later.",
40+
*args,
41+
)

src/posit/connect/hooks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from requests import JSONDecodeError, Response
55

6-
from .errors import ClientError
6+
from .exceptions import ClientError
77

88

99
def handle_errors(response: Response, *args, **kwargs) -> Response:

src/posit/connect/settings.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from typing import Optional
2+
3+
import requests
4+
5+
from .urls import Url
6+
7+
8+
class Settings:
9+
def __init__(self, session: requests.Session, url: Url) -> None:
10+
self.session = session
11+
self.url = url
12+
13+
@property
14+
def version(self) -> Optional[str]:
15+
return self.settings.get("version")
16+
17+
@property
18+
def settings(self) -> dict:
19+
url = self.url + "server_settings"
20+
try:
21+
response = self.session.get(url)
22+
return response.json()
23+
except requests.exceptions.RequestException as e:
24+
import logging
25+
26+
logging.debug(
27+
f"Failed to retrieve server settings from {url}. Error: {str(e)}", exc_info=True
28+
)
29+
30+
return {}

src/posit/connect/version.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from functools import wraps
2+
3+
from packaging.version import Version
4+
5+
from .exceptions import VersionUpgradeRequiredException
6+
7+
8+
def requires_version(expected: str):
9+
"""
10+
Decorator to enforce a minimum version requirement for a method.
11+
12+
This decorator checks the `version` attribute of the class instance and raises a `VersionUpgradeRequiredException` if the version is lower than the specified `expected` version.
13+
14+
Parameters
15+
----------
16+
expected : str
17+
The minimum version required for the decorated method to execute. It is compared against the class instance's `version` attribute.
18+
19+
Returns
20+
-------
21+
function
22+
The wrapped function that enforces the version check.
23+
24+
Raises
25+
------
26+
VersionUpgradeRequiredException
27+
If the version specified in the class instance's `version` attribute is lower than the `expected` version.
28+
29+
Examples
30+
--------
31+
To use this decorator, apply it to any method that requires a minimum version:
32+
33+
>>> class Client:
34+
>>> version = "2024.07.0"
35+
>>>
36+
>>> @requires_version("2024.08.0")
37+
>>> def some_method(self):
38+
>>> pass
39+
>>>
40+
>>> client = Client()
41+
>>> client.some_method()
42+
Traceback (most recent call last):
43+
...
44+
VersionUpgradeRequiredException: This API is not available in Connect version 2024.07.0. Please upgrade to version 2024.08.0 or later."
45+
46+
"""
47+
48+
def decorator(func):
49+
@wraps(func)
50+
def wrapper(self, *args, **kwargs):
51+
if hasattr(self, "version"):
52+
version = getattr(self, "version")
53+
if not version:
54+
return
55+
if Version(version) < Version(expected):
56+
raise VersionUpgradeRequiredException(version, expected)
57+
return func(self, *args, **kwargs)
58+
59+
return wrapper
60+
61+
return decorator

tests/posit/connect/test_errors.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22

3-
from posit.connect.errors import ClientError
3+
from posit.connect.exceptions import ClientError
44

55

66
def test():

tests/posit/connect/test_hooks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from requests import HTTPError, Response
77

88
from posit.connect import Client
9-
from posit.connect.errors import ClientError
9+
from posit.connect.exceptions import ClientError
1010
from posit.connect.hooks import handle_errors
1111

1212

0 commit comments

Comments
 (0)