From c82da53502dbb884fc92f1da094a968c9237927b Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 12 Oct 2023 02:30:30 +0100 Subject: [PATCH] refactor(test): refactor authentication tests (#175) --- README.md | 6 ++-- src/anthropic/_base_client.py | 5 ++- src/anthropic/_client.py | 48 ++++++++++++------------- src/anthropic/_utils/__init__.py | 3 ++ src/anthropic/_utils/_utils.py | 18 ++++++++++ tests/api_resources/test_completions.py | 2 +- tests/api_resources/test_top_level.py | 2 +- tests/test_client.py | 20 ++++------- 8 files changed, 60 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 4a8706e1..9e09eeca 100644 --- a/README.md +++ b/README.md @@ -102,8 +102,10 @@ completion = anthropic.completions.create( print(completion.completion) ``` -While you can provide an `api_key` keyword argument, we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/) -and adding `ANTHROPIC_API_KEY="my api key"` to your `.env` file so that your API Key is not stored in source control. +While you can provide an `api_key` keyword argument, +we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/) +to add `ANTHROPIC_API_KEY="my-anthropic-api-key"` to your `.env` file +so that your API Key is not stored in source control. ## Async usage diff --git a/src/anthropic/_base_client.py b/src/anthropic/_base_client.py index 304bea79..34506122 100644 --- a/src/anthropic/_base_client.py +++ b/src/anthropic/_base_client.py @@ -811,7 +811,10 @@ def close(self) -> None: The client will *not* be usable after this. """ - self._client.close() + # If an error is thrown while constructing a client, self._client + # may not be present + if hasattr(self, "_client"): + self._client.close() def __enter__(self: _T) -> _T: return self diff --git a/src/anthropic/_client.py b/src/anthropic/_client.py index 993c34ec..eb92cab1 100644 --- a/src/anthropic/_client.py +++ b/src/anthropic/_client.py @@ -62,9 +62,9 @@ class Anthropic(SyncAPIClient): def __init__( self, *, - auth_token: str | None = None, + api_key: str | None = os.environ.get("ANTHROPIC_API_KEY", None), + auth_token: str | None = os.environ.get("ANTHROPIC_AUTH_TOKEN", None), base_url: Optional[str] = None, - api_key: Optional[str] = None, timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, @@ -93,11 +93,9 @@ def __init__( - `api_key` from `ANTHROPIC_API_KEY` - `auth_token` from `ANTHROPIC_AUTH_TOKEN` """ - api_key = api_key or os.environ.get("ANTHROPIC_API_KEY", None) self.api_key = api_key - auth_token_envvar = os.environ.get("ANTHROPIC_AUTH_TOKEN", None) - self.auth_token = auth_token or auth_token_envvar or None + self.auth_token = auth_token if base_url is None: base_url = f"https://api.anthropic.com" @@ -126,21 +124,21 @@ def qs(self) -> Querystring: @property def auth_headers(self) -> dict[str, str]: - if self._api_key_header: - return self._api_key_header - if self._auth_token_bearer: - return self._auth_token_bearer + if self._api_key_auth: + return self._api_key_auth + if self._bearer_auth: + return self._bearer_auth return {} @property - def _api_key_header(self) -> dict[str, str]: + def _api_key_auth(self) -> dict[str, str]: api_key = self.api_key if api_key is None: return {} return {"X-Api-Key": api_key} @property - def _auth_token_bearer(self) -> dict[str, str]: + def _bearer_auth(self) -> dict[str, str]: auth_token = self.auth_token if auth_token is None: return {} @@ -172,8 +170,8 @@ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: def copy( self, *, - auth_token: str | None = None, api_key: str | None = None, + auth_token: str | None = None, base_url: str | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, http_client: httpx.Client | None = None, @@ -227,9 +225,9 @@ def copy( http_client = http_client or self._client return self.__class__( + api_key=api_key or self.api_key, auth_token=auth_token or self.auth_token, base_url=base_url or str(self.base_url), - api_key=api_key or self.api_key, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, connection_pool_limits=connection_pool_limits, @@ -312,9 +310,9 @@ class AsyncAnthropic(AsyncAPIClient): def __init__( self, *, - auth_token: str | None = None, + api_key: str | None = os.environ.get("ANTHROPIC_API_KEY", None), + auth_token: str | None = os.environ.get("ANTHROPIC_AUTH_TOKEN", None), base_url: Optional[str] = None, - api_key: Optional[str] = None, timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, @@ -343,11 +341,9 @@ def __init__( - `api_key` from `ANTHROPIC_API_KEY` - `auth_token` from `ANTHROPIC_AUTH_TOKEN` """ - api_key = api_key or os.environ.get("ANTHROPIC_API_KEY", None) self.api_key = api_key - auth_token_envvar = os.environ.get("ANTHROPIC_AUTH_TOKEN", None) - self.auth_token = auth_token or auth_token_envvar or None + self.auth_token = auth_token if base_url is None: base_url = f"https://api.anthropic.com" @@ -376,21 +372,21 @@ def qs(self) -> Querystring: @property def auth_headers(self) -> dict[str, str]: - if self._api_key_header: - return self._api_key_header - if self._auth_token_bearer: - return self._auth_token_bearer + if self._api_key_auth: + return self._api_key_auth + if self._bearer_auth: + return self._bearer_auth return {} @property - def _api_key_header(self) -> dict[str, str]: + def _api_key_auth(self) -> dict[str, str]: api_key = self.api_key if api_key is None: return {} return {"X-Api-Key": api_key} @property - def _auth_token_bearer(self) -> dict[str, str]: + def _bearer_auth(self) -> dict[str, str]: auth_token = self.auth_token if auth_token is None: return {} @@ -422,8 +418,8 @@ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: def copy( self, *, - auth_token: str | None = None, api_key: str | None = None, + auth_token: str | None = None, base_url: str | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, http_client: httpx.AsyncClient | None = None, @@ -477,9 +473,9 @@ def copy( http_client = http_client or self._client return self.__class__( + api_key=api_key or self.api_key, auth_token=auth_token or self.auth_token, base_url=base_url or str(self.base_url), - api_key=api_key or self.api_key, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, connection_pool_limits=connection_pool_limits, diff --git a/src/anthropic/_utils/__init__.py b/src/anthropic/_utils/__init__.py index 679193bd..6d13a362 100644 --- a/src/anthropic/_utils/__init__.py +++ b/src/anthropic/_utils/__init__.py @@ -21,6 +21,9 @@ from ._utils import extract_type_arg as extract_type_arg from ._utils import is_required_type as is_required_type from ._utils import is_annotated_type as is_annotated_type +from ._utils import maybe_coerce_float as maybe_coerce_float +from ._utils import maybe_coerce_boolean as maybe_coerce_boolean +from ._utils import maybe_coerce_integer as maybe_coerce_integer from ._utils import strip_annotated_type as strip_annotated_type from ._transform import PropertyInfo as PropertyInfo from ._transform import transform as transform diff --git a/src/anthropic/_utils/_utils.py b/src/anthropic/_utils/_utils.py index 603f7c10..e43ef6f8 100644 --- a/src/anthropic/_utils/_utils.py +++ b/src/anthropic/_utils/_utils.py @@ -309,6 +309,24 @@ def coerce_boolean(val: str) -> bool: return val == "true" or val == "1" or val == "on" +def maybe_coerce_integer(val: str | None) -> int | None: + if val is None: + return None + return coerce_integer(val) + + +def maybe_coerce_float(val: str | None) -> float | None: + if val is None: + return None + return coerce_float(val) + + +def maybe_coerce_boolean(val: str | None) -> bool | None: + if val is None: + return None + return coerce_boolean(val) + + def removeprefix(string: str, prefix: str) -> str: """Remove a prefix from a string. diff --git a/tests/api_resources/test_completions.py b/tests/api_resources/test_completions.py index 8bf12c45..a6e1334f 100644 --- a/tests/api_resources/test_completions.py +++ b/tests/api_resources/test_completions.py @@ -11,7 +11,7 @@ from anthropic.types import Completion base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") -api_key = os.environ.get("API_KEY", "something1234") +api_key = "my-anthropic-api-key" class TestCompletions: diff --git a/tests/api_resources/test_top_level.py b/tests/api_resources/test_top_level.py index ff25b467..6993b03e 100644 --- a/tests/api_resources/test_top_level.py +++ b/tests/api_resources/test_top_level.py @@ -9,7 +9,7 @@ from anthropic import Anthropic, AsyncAnthropic base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") -api_key = os.environ.get("API_KEY", "something1234") +api_key = "my-anthropic-api-key" class TestTopLevel: diff --git a/tests/test_client.py b/tests/test_client.py index 82e7e88e..74d00345 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -27,7 +27,7 @@ ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") -api_key = os.environ.get("API_KEY", "something1234") +api_key = "my-anthropic-api-key" def _get_params(client: BaseClient) -> dict[str, str]: @@ -52,12 +52,9 @@ def test_copy(self) -> None: copied = self.client.copy() assert id(copied) != id(self.client) - copied = self.client.copy(api_key="my new api key") - assert copied.api_key == "my new api key" - assert self.client.api_key == api_key - - copied = self.client.copy(auth_token="my-auth-token") - assert copied.auth_token == "my-auth-token" + copied = self.client.copy(api_key="another my-anthropic-api-key") + assert copied.api_key == "another my-anthropic-api-key" + assert self.client.api_key == "my-anthropic-api-key" def test_copy_default_options(self) -> None: # options that have a default are overridden correctly @@ -669,12 +666,9 @@ def test_copy(self) -> None: copied = self.client.copy() assert id(copied) != id(self.client) - copied = self.client.copy(api_key="my new api key") - assert copied.api_key == "my new api key" - assert self.client.api_key == api_key - - copied = self.client.copy(auth_token="my-auth-token") - assert copied.auth_token == "my-auth-token" + copied = self.client.copy(api_key="another my-anthropic-api-key") + assert copied.api_key == "another my-anthropic-api-key" + assert self.client.api_key == "my-anthropic-api-key" def test_copy_default_options(self) -> None: # options that have a default are overridden correctly