Skip to content
This repository was archived by the owner on Feb 19, 2025. It is now read-only.

Commit 8f161fb

Browse files
authored
Fix race condition in protected decorator (#14)
* Fix race condition in protected decorator * Bump project version * Fix len8 py312 issue in noxfile * Update changelog
1 parent bd07554 commit 8f161fb

File tree

6 files changed

+41
-12
lines changed

6 files changed

+41
-12
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
# Changelog
22

3+
## v0.7.2 (May 2024)
4+
5+
### Fixes
6+
7+
- Fix race condition for concurrent requests using the `protected` decorator.
8+
9+
### Additions
10+
11+
- The `Client` can now be used as an async context manager which starts
12+
and closes the client automatically.
13+
14+
### Changes
15+
16+
- The `InvalidKeyHandlerT` and `ExcHandlerT` types no longer include `Optional`,
17+
and instead are wrapped in `Optional` in the function signature.
18+
19+
---
20+
321
## v0.7.1 (Feb 2024)
422

523
### Fixes

noxfile.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import functools
4+
import platform
45
from typing import Callable
56
from pathlib import Path
67

@@ -75,10 +76,15 @@ def types(session: nox.Session) -> None:
7576

7677

7778
@nox.session(reuse_venv=True)
78-
@install("black", "len8")
79+
@install("black")
7980
def formatting(session: nox.Session) -> None:
8081
session.run("black", ".", "--check")
81-
session.run("len8")
82+
83+
major, minor, *_ = platform.python_version_tuple()
84+
if major == "3" and int(minor) < 12:
85+
# This is a hack but it doesnt support python 3.12
86+
session.install(DEPS["len8"])
87+
session.run("len8")
8288

8389

8490
@nox.session(reuse_venv=True)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "unkey.py"
3-
version = "0.7.1"
3+
version = "0.7.2"
44
description = "An asynchronous Python SDK for unkey.dev."
55
authors = ["Jonxslays"]
66
license = "GPL-3.0-only"

unkey/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from typing import Final
44

55
__packagename__: Final[str] = "unkey.py"
6-
__version__: Final[str] = "0.7.1"
6+
__version__: Final[str] = "0.7.2"
77
__author__: Final[str] = "Jonxslays"
88
__copyright__: Final[str] = "2023-present Jonxslays"
99
__description__: Final[str] = "An asynchronous Python SDK for unkey.dev."

unkey/client.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ def __init_service(self, service: t.Type[ServiceT]) -> ServiceT:
5151

5252
return service(self._http, self._serializer) # type: ignore[return-value]
5353

54+
async def __aenter__(self) -> Client:
55+
await self.start()
56+
return self
57+
58+
async def __aexit__(self, *_args: t.Any, **_kwargs: t.Any) -> None:
59+
await self.close()
60+
5461
@property
5562
def keys(self) -> services.KeyService:
5663
"""The key service used to make key related requests."""

unkey/decorators.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,18 @@
2626
functions `*args` and `**kwargs`.
2727
"""
2828

29-
InvalidKeyHandlerT = Optional[Callable[[Dict[str, Any], Optional[models.ApiKeyVerification]], Any]]
29+
InvalidKeyHandlerT = Callable[[Dict[str, Any], Optional[models.ApiKeyVerification]], Any]
3030
"""The type of a callback used to handle cases where the key was invalid."""
3131

32-
ExcHandlerT = Optional[Callable[[Exception], Any]]
32+
ExcHandlerT = Callable[[Exception], Any]
3333
"""The type of a callback used to handle exceptions during verification."""
3434

3535

3636
def protected(
3737
api_id: str,
3838
key_extractor: ExtractorT,
39-
on_invalid_key: InvalidKeyHandlerT = None,
40-
on_exc: ExcHandlerT = None,
39+
on_invalid_key: Optional[InvalidKeyHandlerT] = None,
40+
on_exc: Optional[ExcHandlerT] = None,
4141
) -> DecoratorT:
4242
"""A framework agnostic second order decorator that is used to protect
4343
api routes with Unkey key verification.
@@ -82,7 +82,6 @@ def protected(
8282
altered by `on_invalid_key` if it was passed. If verification
8383
succeeds the original functions return value is returned.
8484
"""
85-
_client = client.Client()
8685

8786
def _on_invalid_key(
8887
data: Dict[str, Any], verification: Optional[models.ApiKeyVerification] = None
@@ -108,9 +107,8 @@ async def inner(*args: Any, **kwargs: Any) -> VerificationResponseT[T]:
108107
message = "Failed to extract API key"
109108
return _on_invalid_key({"code": None, "message": message})
110109

111-
await _client.start()
112-
result = await _client.keys.verify_key(key, api_id)
113-
await _client.close()
110+
async with client.Client() as c:
111+
result = await c.keys.verify_key(key, api_id)
114112

115113
if result.is_err:
116114
err = result.unwrap_err()

0 commit comments

Comments
 (0)