Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 13 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ It is generated with [Stainless](https://www.stainlessapi.com/).

## Documentation

The REST API documentation can be found on [docs.brainbase.com](https://docs.brainbase.com). The full API of this library can be found in [api.md](api.md).
The REST API documentation can be found on [docs.usebrainbase.com](https://docs.usebrainbase.com). The full API of this library can be found in [api.md](api.md).

## Installation

Expand All @@ -24,25 +24,32 @@ pip install --pre brainbase-labs
The full API of this library can be found in [api.md](api.md).

```python
import os
from brainbase import Brainbase

client = Brainbase(
bearer_token="My Bearer Token",
api_key=os.environ.get("BRAINBASE_API_KEY"), # This is the default and can be omitted
)

client.workers.list()
```

While you can provide an `api_key` keyword argument,
we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/)
to add `BRAINBASE_API_KEY="My API Key"` to your `.env` file
so that your API Key is not stored in source control.

## Async usage

Simply import `AsyncBrainbase` instead of `Brainbase` and use `await` with each API call:

```python
import os
import asyncio
from brainbase import AsyncBrainbase

client = AsyncBrainbase(
bearer_token="My Bearer Token",
api_key=os.environ.get("BRAINBASE_API_KEY"), # This is the default and can be omitted
)


Expand Down Expand Up @@ -77,9 +84,7 @@ All errors inherit from `brainbase.APIError`.
import brainbase
from brainbase import Brainbase

client = Brainbase(
bearer_token="My Bearer Token",
)
client = Brainbase()

try:
client.workers.list()
Expand Down Expand Up @@ -122,7 +127,6 @@ from brainbase import Brainbase
client = Brainbase(
# default is 2
max_retries=0,
bearer_token="My Bearer Token",
)

# Or, configure per-request:
Expand All @@ -141,13 +145,11 @@ from brainbase import Brainbase
client = Brainbase(
# 20 seconds (default is 1 minute)
timeout=20.0,
bearer_token="My Bearer Token",
)

# More granular control:
client = Brainbase(
timeout=httpx.Timeout(60.0, read=5.0, write=10.0, connect=2.0),
bearer_token="My Bearer Token",
)

# Override per-request:
Expand Down Expand Up @@ -191,9 +193,7 @@ The "raw" Response object can be accessed by prefixing `.with_raw_response.` to
```py
from brainbase import Brainbase

client = Brainbase(
bearer_token="My Bearer Token",
)
client = Brainbase()
response = client.workers.with_raw_response.list()
print(response.headers.get('X-My-Header'))

Expand Down Expand Up @@ -274,7 +274,6 @@ client = Brainbase(
proxy="http://my.test.proxy.example.com",
transport=httpx.HTTPTransport(local_address="0.0.0.0"),
),
bearer_token="My Bearer Token",
)
```

Expand All @@ -291,9 +290,7 @@ By default the library closes underlying HTTP connections whenever the client is
```py
from brainbase import Brainbase

with Brainbase(
bearer_token="My Bearer Token",
) as client:
with Brainbase() as client:
# make requests here
...

Expand Down
52 changes: 32 additions & 20 deletions src/brainbase/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,12 @@ class Brainbase(SyncAPIClient):
with_streaming_response: BrainbaseWithStreamedResponse

# client options
bearer_token: str
api_key: str

def __init__(
self,
*,
bearer_token: str | None = None,
api_key: str | None = None,
base_url: str | httpx.URL | None = None,
timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
max_retries: int = DEFAULT_MAX_RETRIES,
Expand All @@ -78,15 +78,15 @@ def __init__(
) -> None:
"""Construct a new synchronous brainbase client instance.

This automatically infers the `bearer_token` argument from the `BEARER_TOKEN` environment variable if it is not provided.
This automatically infers the `api_key` argument from the `BRAINBASE_API_KEY` environment variable if it is not provided.
"""
if bearer_token is None:
bearer_token = os.environ.get("BEARER_TOKEN")
if bearer_token is None:
if api_key is None:
api_key = os.environ.get("BRAINBASE_API_KEY")
if api_key is None:
raise BrainbaseError(
"The bearer_token client option must be set either by passing bearer_token to the client or by setting the BEARER_TOKEN environment variable"
"The api_key client option must be set either by passing api_key to the client or by setting the BRAINBASE_API_KEY environment variable"
)
self.bearer_token = bearer_token
self.api_key = api_key

if base_url is None:
base_url = os.environ.get("BRAINBASE_BASE_URL")
Expand All @@ -113,6 +113,12 @@ def __init__(
def qs(self) -> Querystring:
return Querystring(array_format="comma")

@property
@override
def auth_headers(self) -> dict[str, str]:
api_key = self.api_key
return {"x-api-key": api_key}

@property
@override
def default_headers(self) -> dict[str, str | Omit]:
Expand All @@ -125,7 +131,7 @@ def default_headers(self) -> dict[str, str | Omit]:
def copy(
self,
*,
bearer_token: str | None = None,
api_key: str | None = None,
base_url: str | httpx.URL | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
http_client: httpx.Client | None = None,
Expand Down Expand Up @@ -159,7 +165,7 @@ def copy(

http_client = http_client or self._client
return self.__class__(
bearer_token=bearer_token or self.bearer_token,
api_key=api_key or self.api_key,
base_url=base_url or self.base_url,
timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
http_client=http_client,
Expand Down Expand Up @@ -213,12 +219,12 @@ class AsyncBrainbase(AsyncAPIClient):
with_streaming_response: AsyncBrainbaseWithStreamedResponse

# client options
bearer_token: str
api_key: str

def __init__(
self,
*,
bearer_token: str | None = None,
api_key: str | None = None,
base_url: str | httpx.URL | None = None,
timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
max_retries: int = DEFAULT_MAX_RETRIES,
Expand All @@ -240,15 +246,15 @@ def __init__(
) -> None:
"""Construct a new async brainbase client instance.

This automatically infers the `bearer_token` argument from the `BEARER_TOKEN` environment variable if it is not provided.
This automatically infers the `api_key` argument from the `BRAINBASE_API_KEY` environment variable if it is not provided.
"""
if bearer_token is None:
bearer_token = os.environ.get("BEARER_TOKEN")
if bearer_token is None:
if api_key is None:
api_key = os.environ.get("BRAINBASE_API_KEY")
if api_key is None:
raise BrainbaseError(
"The bearer_token client option must be set either by passing bearer_token to the client or by setting the BEARER_TOKEN environment variable"
"The api_key client option must be set either by passing api_key to the client or by setting the BRAINBASE_API_KEY environment variable"
)
self.bearer_token = bearer_token
self.api_key = api_key

if base_url is None:
base_url = os.environ.get("BRAINBASE_BASE_URL")
Expand All @@ -275,6 +281,12 @@ def __init__(
def qs(self) -> Querystring:
return Querystring(array_format="comma")

@property
@override
def auth_headers(self) -> dict[str, str]:
api_key = self.api_key
return {"x-api-key": api_key}

@property
@override
def default_headers(self) -> dict[str, str | Omit]:
Expand All @@ -287,7 +299,7 @@ def default_headers(self) -> dict[str, str | Omit]:
def copy(
self,
*,
bearer_token: str | None = None,
api_key: str | None = None,
base_url: str | httpx.URL | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
http_client: httpx.AsyncClient | None = None,
Expand Down Expand Up @@ -321,7 +333,7 @@ def copy(

http_client = http_client or self._client
return self.__class__(
bearer_token=bearer_token or self.bearer_token,
api_key=api_key or self.api_key,
base_url=base_url or self.base_url,
timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
http_client=http_client,
Expand Down
8 changes: 3 additions & 5 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def pytest_collection_modifyitems(items: list[pytest.Function]) -> None:

base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")

bearer_token = "My Bearer Token"
api_key = "My API Key"


@pytest.fixture(scope="session")
Expand All @@ -37,7 +37,7 @@ def client(request: FixtureRequest) -> Iterator[Brainbase]:
if not isinstance(strict, bool):
raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}")

with Brainbase(base_url=base_url, bearer_token=bearer_token, _strict_response_validation=strict) as client:
with Brainbase(base_url=base_url, api_key=api_key, _strict_response_validation=strict) as client:
yield client


Expand All @@ -47,7 +47,5 @@ async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncBrainbase]
if not isinstance(strict, bool):
raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}")

async with AsyncBrainbase(
base_url=base_url, bearer_token=bearer_token, _strict_response_validation=strict
) as client:
async with AsyncBrainbase(base_url=base_url, api_key=api_key, _strict_response_validation=strict) as client:
yield client
Loading