Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: add legacy type compatibility aliases for v1.x migration #69

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
zeke wants to merge 3 commits into main from dp-659-legacy-type-aliases
Closed
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
3 changes: 2 additions & 1 deletion requirements-dev.lock
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,10 @@ mypy-extensions==1.0.0
nodeenv==1.8.0
# via pyright
nox==2023年4月22日
packaging==23.2
packaging==25.0
# via nox
# via pytest
# via replicate
platformdirs==3.11.0
# via virtualenv
pluggy==1.5.0
Expand Down
2 changes: 2 additions & 0 deletions requirements.lock
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ idna==3.4
multidict==6.4.4
# via aiohttp
# via yarl
packaging==25.0
# via replicate
propcache==0.3.1
# via aiohttp
# via yarl
Expand Down
44 changes: 44 additions & 0 deletions src/replicate/_client.py
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ def __init__(
self,
*,
bearer_token: str | None = None,
api_token: str | None = None, # Legacy compatibility parameter
base_url: str | httpx.URL | None = None,
timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
max_retries: int = DEFAULT_MAX_RETRIES,
Expand All @@ -124,7 +125,17 @@ def __init__(
"""Construct a new synchronous Replicate client instance.

This automatically infers the `bearer_token` argument from the `REPLICATE_API_TOKEN` environment variable if it is not provided.

For legacy compatibility, you can also pass `api_token` instead of `bearer_token`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, not a fan of the term "legacy" here, that won't age too well (legacy is relative). I think it would be better to mention a major version number instead?

zeke reacted with thumbs up emoji
"""
# Handle legacy api_token parameter
if api_token is not None and bearer_token is not None:
raise ReplicateError(
"Cannot specify both 'bearer_token' and 'api_token'. Please use 'bearer_token' (recommended) or 'api_token' for legacy compatibility."
)
if api_token is not None:
bearer_token = api_token

if bearer_token is None:
bearer_token = _get_api_token_from_environment()
if bearer_token is None:
Expand Down Expand Up @@ -324,6 +335,7 @@ def copy(
self,
*,
bearer_token: str | None = None,
api_token: str | None = None, # Legacy compatibility parameter
base_url: str | httpx.URL | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
http_client: httpx.Client | None = None,
Expand All @@ -336,7 +348,17 @@ def copy(
) -> Self:
"""
Create a new client instance re-using the same options given to the current client with optional overriding.

For legacy compatibility, you can also pass `api_token` instead of `bearer_token`.
"""
# Handle legacy api_token parameter
if api_token is not None and bearer_token is not None:
raise ValueError(
"Cannot specify both 'bearer_token' and 'api_token'. Please use 'bearer_token' (recommended) or 'api_token' for legacy compatibility."
)
if api_token is not None:
bearer_token = api_token

if default_headers is not None and set_default_headers is not None:
raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive")

Expand Down Expand Up @@ -477,6 +499,7 @@ def __init__(
self,
*,
bearer_token: str | None = None,
api_token: str | None = None, # Legacy compatibility parameter
base_url: str | httpx.URL | None = None,
timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
max_retries: int = DEFAULT_MAX_RETRIES,
Expand All @@ -499,7 +522,17 @@ def __init__(
"""Construct a new async AsyncReplicate client instance.

This automatically infers the `bearer_token` argument from the `REPLICATE_API_TOKEN` environment variable if it is not provided.

For legacy compatibility, you can also pass `api_token` instead of `bearer_token`.
"""
# Handle legacy api_token parameter
if api_token is not None and bearer_token is not None:
raise ReplicateError(
"Cannot specify both 'bearer_token' and 'api_token'. Please use 'bearer_token' (recommended) or 'api_token' for legacy compatibility."
)
if api_token is not None:
bearer_token = api_token

if bearer_token is None:
bearer_token = _get_api_token_from_environment()
if bearer_token is None:
Expand Down Expand Up @@ -699,6 +732,7 @@ def copy(
self,
*,
bearer_token: str | None = None,
api_token: str | None = None, # Legacy compatibility parameter
base_url: str | httpx.URL | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
http_client: httpx.AsyncClient | None = None,
Expand All @@ -711,7 +745,17 @@ def copy(
) -> Self:
"""
Create a new client instance re-using the same options given to the current client with optional overriding.

For legacy compatibility, you can also pass `api_token` instead of `bearer_token`.
"""
# Handle legacy api_token parameter
if api_token is not None and bearer_token is not None:
raise ValueError(
"Cannot specify both 'bearer_token' and 'api_token'. Please use 'bearer_token' (recommended) or 'api_token' for legacy compatibility."
)
if api_token is not None:
bearer_token = api_token

if default_headers is not None and set_default_headers is not None:
raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive")

Expand Down
35 changes: 35 additions & 0 deletions src/replicate/model.py
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""
Legacy compatibility module for replicate-python v1.x type names.

This module provides backward compatibility for code that imports types
using the old v1.x import paths like:
from replicate.model import Model
from replicate.model import Version
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from .types import Prediction as Prediction

# Import the actual types from their new locations
from .lib._models import Model as Model, Version as Version

# Also provide aliases for the response types for type checking
if TYPE_CHECKING:
from .types import ModelGetResponse as ModelResponse
from .types.models.version_get_response import VersionGetResponse as VersionResponse
else:
# At runtime, make the response types available under legacy names
from .types import ModelGetResponse as ModelResponse
from .types.models.version_get_response import VersionGetResponse as VersionResponse

__all__ = [
"Model",
"Version",
"Prediction",
"ModelResponse",
"VersionResponse",
]

88 changes: 88 additions & 0 deletions tests/test_api_token_compatibility.py
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"""Tests for api_token legacy compatibility during client instantiation."""

from __future__ import annotations

import pytest

from replicate import Replicate, AsyncReplicate, ReplicateError
from replicate._client import Client


class TestApiTokenCompatibility:
"""Test that api_token parameter works as a legacy compatibility option."""

def test_sync_client_with_api_token(self) -> None:
"""Test that Replicate accepts api_token parameter."""
client = Replicate(api_token="test_token_123")
assert client.bearer_token == "test_token_123"

def test_async_client_with_api_token(self) -> None:
"""Test that AsyncReplicate accepts api_token parameter."""
client = AsyncReplicate(api_token="test_token_123")
assert client.bearer_token == "test_token_123"

def test_sync_client_with_bearer_token(self) -> None:
"""Test that Replicate still accepts bearer_token parameter."""
client = Replicate(bearer_token="test_token_123")
assert client.bearer_token == "test_token_123"

def test_async_client_with_bearer_token(self) -> None:
"""Test that AsyncReplicate still accepts bearer_token parameter."""
client = AsyncReplicate(bearer_token="test_token_123")
assert client.bearer_token == "test_token_123"

def test_sync_client_both_tokens_error(self) -> None:
"""Test that providing both api_token and bearer_token raises an error."""
with pytest.raises(ReplicateError, match="Cannot specify both 'bearer_token' and 'api_token'"):
Replicate(api_token="test_api", bearer_token="test_bearer")

def test_async_client_both_tokens_error(self) -> None:
"""Test that providing both api_token and bearer_token raises an error."""
with pytest.raises(ReplicateError, match="Cannot specify both 'bearer_token' and 'api_token'"):
AsyncReplicate(api_token="test_api", bearer_token="test_bearer")

def test_sync_client_no_token_with_env(self, monkeypatch: pytest.MonkeyPatch) -> None:
"""Test that client reads from environment when no token is provided."""
monkeypatch.setenv("REPLICATE_API_TOKEN", "env_token_123")
client = Replicate()
assert client.bearer_token == "env_token_123"

def test_async_client_no_token_with_env(self, monkeypatch: pytest.MonkeyPatch) -> None:
"""Test that async client reads from environment when no token is provided."""
monkeypatch.setenv("REPLICATE_API_TOKEN", "env_token_123")
client = AsyncReplicate()
assert client.bearer_token == "env_token_123"

def test_sync_client_no_token_no_env(self, monkeypatch: pytest.MonkeyPatch) -> None:
"""Test that client raises error when no token is provided and env is not set."""
monkeypatch.delenv("REPLICATE_API_TOKEN", raising=False)
with pytest.raises(ReplicateError, match="The bearer_token client option must be set"):
Replicate()

def test_async_client_no_token_no_env(self, monkeypatch: pytest.MonkeyPatch) -> None:
"""Test that async client raises error when no token is provided and env is not set."""
monkeypatch.delenv("REPLICATE_API_TOKEN", raising=False)
with pytest.raises(ReplicateError, match="The bearer_token client option must be set"):
AsyncReplicate()

def test_legacy_client_alias(self) -> None:
"""Test that legacy Client import still works as an alias."""
assert Client is Replicate

def test_legacy_client_with_api_token(self) -> None:
"""Test that legacy Client alias works with api_token parameter."""
client = Client(api_token="test_token_123")
assert client.bearer_token == "test_token_123"
assert isinstance(client, Replicate)

def test_api_token_overrides_env(self, monkeypatch: pytest.MonkeyPatch) -> None:
"""Test that explicit api_token overrides environment variable."""
monkeypatch.setenv("REPLICATE_API_TOKEN", "env_token")
client = Replicate(api_token="explicit_token")
assert client.bearer_token == "explicit_token"

def test_bearer_token_overrides_env(self, monkeypatch: pytest.MonkeyPatch) -> None:
"""Test that explicit bearer_token overrides environment variable."""
monkeypatch.setenv("REPLICATE_API_TOKEN", "env_token")
client = Replicate(bearer_token="explicit_token")
assert client.bearer_token == "explicit_token"
Loading

AltStyle によって変換されたページ (->オリジナル) /