Skip to content

Commit ff09b0c

Browse files
authored
Merge pull request #9 from kbase/dev-client
Refactor modules to make imports easier
2 parents e9dd3a2 + 2f2d27b commit ff09b0c

File tree

8 files changed

+87
-104
lines changed

8 files changed

+87
-104
lines changed

scripts/process_unasync.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ def main():
1616

1717
rules = [
1818
unasync.Rule(
19-
fromdir="/src/kbase/auth/_async/",
20-
todir="/src/kbase/auth/_sync/",
19+
fromdir="/src/kbase/_auth/_async/",
20+
todir="/src/kbase/_auth/_sync/",
2121
additional_replacements=additional_replacements,
2222
),
2323
]
2424

2525
filepaths = [
26-
str(Path(__file__).parent.parent / "src" / "kbase" / "auth" / "_async" / "client.py")
26+
str(Path(__file__).parent.parent / "src" / "kbase" / "_auth" / "_async" / "client.py")
2727
]
2828

2929
unasync.unasync_files(filepaths, rules)

src/kbase/auth/_async/client.py renamed to src/kbase/_auth/_async/client.py

Lines changed: 4 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,55 +8,20 @@
88
# the sync client.
99

1010
from cacheout.lru import LRUCache
11-
from dataclasses import dataclass, fields
1211
import httpx
1312
import logging
1413
import time
1514
from typing import Self, Callable
16-
from uuid import UUID
1715

18-
from kbase.auth.exceptions import InvalidTokenError, InvalidUserError
16+
from kbase._auth.exceptions import InvalidTokenError, InvalidUserError
17+
from kbase._auth.models import Token, User, VALID_TOKEN_FIELDS, VALID_USER_FIELDS
1918

2019
# TODO PUBLISH make a pypi kbase org and publish there
2120
# TODO RELIABILITY could add retries for these methods, tenacity looks useful
2221
# should be safe since they're all read only
23-
# TODO NOW CODE make a kbase/auth.py module, move other code into _auth, and import everything
24-
# TODO NOW CODE move Token and User into a common class
2522
# We might want to expand exceptions to include the request ID for debugging purposes
2623

2724

28-
@dataclass
29-
class Token:
30-
""" A KBase authentication token. """
31-
id: UUID
32-
""" The token's unique ID. """
33-
user: str
34-
""" The username of the user associated with the token. """
35-
created: int
36-
""" The time the token was created in epoch milliseconds. """
37-
expires: int
38-
""" The time the token expires in epoch milliseconds. """
39-
cachefor: int
40-
""" The time the token should be cached for in milliseconds. """
41-
# TODO MFA add mfa info when the auth service supports it
42-
43-
_VALID_TOKEN_FIELDS = {f.name for f in fields(Token)}
44-
45-
46-
@dataclass
47-
class User:
48-
""" Information about a KBase user. """
49-
user: str
50-
""" The username of the user associated with the token. """
51-
customroles: list[str]
52-
""" The Auth2 custom roles the user possesses. """
53-
# Not seeing any other fields that are generally useful right now
54-
# Don't really want to expose idents unless there's a very good reason
55-
56-
57-
_VALID_USER_FIELDS = {f.name for f in fields(User)}
58-
59-
6025
def _require_string(putative: str, name: str) -> str:
6126
if not isinstance(putative, str) or not putative.strip():
6227
raise ValueError(f"{name} is required and cannot be a whitespace only string")
@@ -169,7 +134,7 @@ async def get_token(self, token: str, on_cache_miss: Callable[[], None]=None) ->
169134
if on_cache_miss:
170135
on_cache_miss()
171136
res = await self._get(self._token_url, headers={"Authorization": token})
172-
tk = Token(**{k: v for k, v in res.items() if k in _VALID_TOKEN_FIELDS})
137+
tk = Token(**{k: v for k, v in res.items() if k in VALID_TOKEN_FIELDS})
173138
# TODO TEST later may want to add tests that change the cachefor value.
174139
self._token_cache.set(token, tk, ttl=tk.cachefor / 1000)
175140
return tk
@@ -193,7 +158,7 @@ async def get_user(self, token: str, on_cache_miss: Callable[[], None]=None) ->
193158
on_cache_miss()
194159
tk = await self.get_token(token)
195160
res = await self._get(self._me_url, headers={"Authorization": token})
196-
u = User(**{k: v for k, v in res.items() if k in _VALID_USER_FIELDS})
161+
u = User(**{k: v for k, v in res.items() if k in VALID_USER_FIELDS})
197162
# TODO TEST later may want to add tests that change the cachefor value.
198163
self._user_cache.set(token, u, ttl=tk.cachefor / 1000)
199164
return u

src/kbase/auth/_sync/client.py renamed to src/kbase/_auth/_sync/client.py

Lines changed: 4 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,55 +8,20 @@
88
# the sync client.
99

1010
from cacheout.lru import LRUCache
11-
from dataclasses import dataclass, fields
1211
import httpx
1312
import logging
1413
import time
1514
from typing import Self, Callable
16-
from uuid import UUID
1715

18-
from kbase.auth.exceptions import InvalidTokenError, InvalidUserError
16+
from kbase._auth.exceptions import InvalidTokenError, InvalidUserError
17+
from kbase._auth.models import Token, User, VALID_TOKEN_FIELDS, VALID_USER_FIELDS
1918

2019
# TODO PUBLISH make a pypi kbase org and publish there
2120
# TODO RELIABILITY could add retries for these methods, tenacity looks useful
2221
# should be safe since they're all read only
23-
# TODO NOW CODE make a kbase/auth.py module, move other code into _auth, and import everything
24-
# TODO NOW CODE move Token and User into a common class
2522
# We might want to expand exceptions to include the request ID for debugging purposes
2623

2724

28-
@dataclass
29-
class Token:
30-
""" A KBase authentication token. """
31-
id: UUID
32-
""" The token's unique ID. """
33-
user: str
34-
""" The username of the user associated with the token. """
35-
created: int
36-
""" The time the token was created in epoch milliseconds. """
37-
expires: int
38-
""" The time the token expires in epoch milliseconds. """
39-
cachefor: int
40-
""" The time the token should be cached for in milliseconds. """
41-
# TODO MFA add mfa info when the auth service supports it
42-
43-
_VALID_TOKEN_FIELDS = {f.name for f in fields(Token)}
44-
45-
46-
@dataclass
47-
class User:
48-
""" Information about a KBase user. """
49-
user: str
50-
""" The username of the user associated with the token. """
51-
customroles: list[str]
52-
""" The Auth2 custom roles the user possesses. """
53-
# Not seeing any other fields that are generally useful right now
54-
# Don't really want to expose idents unless there's a very good reason
55-
56-
57-
_VALID_USER_FIELDS = {f.name for f in fields(User)}
58-
59-
6025
def _require_string(putative: str, name: str) -> str:
6126
if not isinstance(putative, str) or not putative.strip():
6227
raise ValueError(f"{name} is required and cannot be a whitespace only string")
@@ -169,7 +134,7 @@ def get_token(self, token: str, on_cache_miss: Callable[[], None]=None) -> Token
169134
if on_cache_miss:
170135
on_cache_miss()
171136
res = self._get(self._token_url, headers={"Authorization": token})
172-
tk = Token(**{k: v for k, v in res.items() if k in _VALID_TOKEN_FIELDS})
137+
tk = Token(**{k: v for k, v in res.items() if k in VALID_TOKEN_FIELDS})
173138
# TODO TEST later may want to add tests that change the cachefor value.
174139
self._token_cache.set(token, tk, ttl=tk.cachefor / 1000)
175140
return tk
@@ -193,7 +158,7 @@ def get_user(self, token: str, on_cache_miss: Callable[[], None]=None) -> User:
193158
on_cache_miss()
194159
tk = self.get_token(token)
195160
res = self._get(self._me_url, headers={"Authorization": token})
196-
u = User(**{k: v for k, v in res.items() if k in _VALID_USER_FIELDS})
161+
u = User(**{k: v for k, v in res.items() if k in VALID_USER_FIELDS})
197162
# TODO TEST later may want to add tests that change the cachefor value.
198163
self._user_cache.set(token, u, ttl=tk.cachefor / 1000)
199164
return u
File renamed without changes.

src/kbase/_auth/models.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""
2+
Data classes for the clients.
3+
"""
4+
5+
from dataclasses import dataclass, fields
6+
from uuid import UUID
7+
8+
@dataclass
9+
class Token:
10+
""" A KBase authentication token. """
11+
id: UUID
12+
""" The token's unique ID. """
13+
user: str
14+
""" The username of the user associated with the token. """
15+
created: int
16+
""" The time the token was created in epoch milliseconds. """
17+
expires: int
18+
""" The time the token expires in epoch milliseconds. """
19+
cachefor: int
20+
""" The time the token should be cached for in milliseconds. """
21+
# TODO MFA add mfa info when the auth service supports it
22+
23+
VALID_TOKEN_FIELDS: set[str] = {f.name for f in fields(Token)}
24+
"""
25+
The field names for the Token dataclass.
26+
"""
27+
28+
29+
@dataclass
30+
class User:
31+
""" Information about a KBase user. """
32+
user: str
33+
""" The username of the user associated with the token. """
34+
customroles: list[str]
35+
""" The Auth2 custom roles the user possesses. """
36+
# Not seeing any other fields that are generally useful right now
37+
# Don't really want to expose idents unless there's a very good reason
38+
39+
40+
VALID_USER_FIELDS: set[str] = {f.name for f in fields(User)}
41+
"""
42+
The field names for the user dataclass.
43+
"""

src/kbase/auth.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"""
2+
The aync and sync versions of the KBase Auth Client.
3+
"""
4+
5+
from kbase._auth._async.client import AsyncKBaseAuthClient # @UnusedImport
6+
from kbase._auth._sync.client import KBaseAuthClient # @UnusedImport
7+
from kbase._auth.exceptions import (
8+
AuthenticationError, # @UnusedImport
9+
InvalidTokenError, # @UnusedImport
10+
InvalidUserError, # @UnusedImport
11+
)
12+
from kbase._auth.models import (
13+
Token, # @UnusedImport
14+
User, # @UnusedImport
15+
)

src/kbase/auth/client.py

Lines changed: 0 additions & 6 deletions
This file was deleted.

test/test_client.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,14 @@
55

66
from conftest import AUTH_URL, AUTH_VERSION
77

8-
from kbase.auth.client import KBaseAuthClient, AsyncKBaseAuthClient
9-
from kbase.auth.exceptions import InvalidTokenError, InvalidUserError
8+
from kbase.auth import (
9+
AsyncKBaseAuthClient,
10+
InvalidTokenError,
11+
InvalidUserError,
12+
KBaseAuthClient,
13+
Token,
14+
User,
15+
)
1016

1117

1218
async def _create_fail(url: str, expected: Exception, cachesize=1, timer=time.time):
@@ -90,15 +96,17 @@ async def test_get_token_basic(auth_users):
9096
async with await AsyncKBaseAuthClient.create(AUTH_URL) as cli:
9197
t2 = await cli.get_token(auth_users["user_random1"])
9298

99+
assert t1 == Token(
100+
id=t1.id, user="user", cachefor=300000, created=t1.created, expires=t1.expires
101+
)
93102
assert is_valid_uuid(t1.id)
94-
assert t1.user == "user"
95-
assert t1.cachefor == 300000
96103
assert time_close_to_now(t1.created, 10)
97104
assert t1.expires - t1.created == 3600000
98105

106+
assert t2 == Token(
107+
id=t2.id, user="user_random1", cachefor=300000, created=t2.created, expires=t2.expires
108+
)
99109
assert is_valid_uuid(t2.id)
100-
assert t2.user == "user_random1"
101-
assert t2.cachefor == 300000
102110
assert time_close_to_now(t2.created, 10)
103111
assert t2.expires - t2.created == 3600000
104112

@@ -222,17 +230,10 @@ async def test_get_user_basic(auth_users):
222230
u3 = await cli.get_user(auth_users["user_random1"])
223231
u4 = await cli.get_user(auth_users["user_random2"])
224232

225-
assert u1.user == "user"
226-
assert u1.customroles == []
227-
228-
assert u2.user == "user_all"
229-
assert u2.customroles == ["random1", "random2"]
230-
231-
assert u3.user == "user_random1"
232-
assert u3.customroles == ["random1"]
233-
234-
assert u4.user == "user_random2"
235-
assert u4.customroles == ["random2"]
233+
assert u1 == User(user="user", customroles=[])
234+
assert u2 == User(user="user_all", customroles=["random1", "random2"])
235+
assert u3 == User(user="user_random1", customroles=["random1"])
236+
assert u4 == User(user="user_random2", customroles=["random2"])
236237

237238

238239
@pytest.mark.asyncio

0 commit comments

Comments
 (0)