Skip to content

Commit 617b0ff

Browse files
committed
feat: add legacy type compatibility aliases for v1.x migration
This PR addresses DP-659 by adding backward compatibility aliases for legacy v1.x type names to ease migration to v2.x. ## Changes - Created `replicate/model.py` module with legacy import paths - Aliased `Model`, `Version`, and `Prediction` types from their current locations - Added `ModelResponse` and `VersionResponse` aliases for response types - Added comprehensive tests for isinstance() checks and type annotations ## Impact This allows existing code using v1.x type imports like `from replicate.model import Model` to continue working with v2.x, reducing breaking changes during migration.
1 parent 8c05e64 commit 617b0ff

File tree

5 files changed

+199
-3
lines changed

5 files changed

+199
-3
lines changed

requirements-dev.lock

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,10 @@ mypy-extensions==1.0.0
7878
nodeenv==1.8.0
7979
# via pyright
8080
nox==2023.4.22
81-
packaging==23.2
81+
packaging==25.0
8282
# via nox
8383
# via pytest
84+
# via replicate
8485
platformdirs==3.11.0
8586
# via virtualenv
8687
pluggy==1.5.0

requirements.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ idna==3.4
5252
multidict==6.4.4
5353
# via aiohttp
5454
# via yarl
55+
packaging==25.0
56+
# via replicate
5557
propcache==0.3.1
5658
# via aiohttp
5759
# via yarl

src/replicate/model.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""
2+
Legacy compatibility module for replicate-python v1.x type names.
3+
4+
This module provides backward compatibility for code that imports types
5+
using the old v1.x import paths like:
6+
from replicate.model import Model
7+
from replicate.model import Version
8+
"""
9+
10+
from __future__ import annotations
11+
12+
from typing import TYPE_CHECKING
13+
14+
from .types import Prediction as Prediction
15+
16+
# Import the actual types from their new locations
17+
from .lib._models import Model as Model, Version as Version
18+
19+
# Also provide aliases for the response types for type checking
20+
if TYPE_CHECKING:
21+
from .types import ModelGetResponse as ModelResponse
22+
from .types.models.version_get_response import VersionGetResponse as VersionResponse
23+
else:
24+
# At runtime, make the response types available under legacy names
25+
from .types import ModelGetResponse as ModelResponse
26+
from .types.models.version_get_response import VersionGetResponse as VersionResponse
27+
28+
__all__ = [
29+
"Model",
30+
"Version",
31+
"Prediction",
32+
"ModelResponse",
33+
"VersionResponse",
34+
]
35+

tests/test_api_token_compatibility.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
from __future__ import annotations
44

5-
import os
65
import pytest
76

87
from replicate import Replicate, AsyncReplicate, ReplicateError
@@ -86,4 +85,4 @@ def test_bearer_token_overrides_env(self, monkeypatch: pytest.MonkeyPatch) -> No
8685
"""Test that explicit bearer_token overrides environment variable."""
8786
monkeypatch.setenv("REPLICATE_API_TOKEN", "env_token")
8887
client = Replicate(bearer_token="explicit_token")
89-
assert client.bearer_token == "explicit_token"
88+
assert client.bearer_token == "explicit_token"

tests/test_legacy_compatibility.py

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
"""Tests for legacy v1.x type compatibility."""
2+
3+
from __future__ import annotations
4+
5+
from typing import TYPE_CHECKING
6+
7+
from replicate.types import Prediction, ModelGetResponse
8+
from replicate.types.models.version_get_response import VersionGetResponse
9+
10+
11+
def test_legacy_model_imports():
12+
"""Test that legacy import paths work."""
13+
# Test importing Model from legacy path
14+
from replicate.model import Model
15+
from replicate.lib._models import Model as LibModel
16+
17+
# Verify they are the same class
18+
assert Model is LibModel
19+
20+
21+
def test_legacy_version_imports():
22+
"""Test that legacy Version import paths work."""
23+
# Test importing Version from legacy path
24+
from replicate.model import Version
25+
from replicate.lib._models import Version as LibVersion
26+
27+
# Verify they are the same class
28+
assert Version is LibVersion
29+
30+
31+
def test_legacy_prediction_imports():
32+
"""Test that legacy Prediction import paths work."""
33+
# Test importing Prediction from legacy path
34+
from replicate.model import Prediction as LegacyPrediction
35+
from replicate.types import Prediction as TypesPrediction
36+
37+
# Verify they are the same class
38+
assert LegacyPrediction is TypesPrediction
39+
40+
41+
def test_legacy_response_type_imports():
42+
"""Test that legacy response type aliases work."""
43+
# Test importing response types from legacy path
44+
from replicate.model import ModelResponse, VersionResponse
45+
46+
# Verify they are the correct types
47+
assert ModelResponse is ModelGetResponse
48+
assert VersionResponse is VersionGetResponse
49+
50+
51+
def test_legacy_isinstance_checks_with_model():
52+
"""Test that isinstance checks work with legacy Model type."""
53+
from replicate.model import Model
54+
55+
# Create an instance
56+
model = Model(owner="test", name="model")
57+
58+
# Test isinstance check
59+
assert isinstance(model, Model)
60+
61+
62+
def test_legacy_isinstance_checks_with_version():
63+
"""Test that isinstance checks work with legacy Version type."""
64+
import datetime
65+
66+
from replicate.model import Version
67+
68+
# Create a Version instance
69+
version = Version(id="test-version-id", created_at=datetime.datetime.now(), cog_version="0.8.0", openapi_schema={})
70+
71+
# Test isinstance check
72+
assert isinstance(version, Version)
73+
74+
75+
def test_legacy_isinstance_checks_with_prediction():
76+
"""Test that isinstance checks work with legacy Prediction type."""
77+
import datetime
78+
79+
from replicate.model import Prediction
80+
81+
# Create a Prediction instance using construct to bypass validation
82+
prediction = Prediction.construct(
83+
id="test-prediction-id",
84+
created_at=datetime.datetime.now(),
85+
data_removed=False,
86+
input={},
87+
model="test/model",
88+
output=None,
89+
status="succeeded",
90+
urls={
91+
"cancel": "https://example.com/cancel",
92+
"get": "https://example.com/get",
93+
"web": "https://example.com/web",
94+
},
95+
version="test-version",
96+
)
97+
98+
# Test isinstance check
99+
assert isinstance(prediction, Prediction)
100+
101+
102+
def test_legacy_isinstance_checks_with_model_response():
103+
"""Test that isinstance checks work with ModelResponse alias."""
104+
from replicate.model import ModelResponse
105+
106+
# Create a ModelGetResponse instance
107+
model = ModelGetResponse.construct(name="test-model", owner="test-owner")
108+
109+
# Test isinstance check with both the alias and the actual type
110+
assert isinstance(model, ModelResponse)
111+
assert isinstance(model, ModelGetResponse)
112+
113+
114+
def test_legacy_isinstance_checks_with_version_response():
115+
"""Test that isinstance checks work with VersionResponse alias."""
116+
import datetime
117+
118+
from replicate.model import VersionResponse
119+
120+
# Create a VersionGetResponse instance
121+
version = VersionGetResponse.construct(
122+
id="test-version-id", created_at=datetime.datetime.now(), cog_version="0.8.0", openapi_schema={}
123+
)
124+
125+
# Test isinstance check with both the alias and the actual type
126+
assert isinstance(version, VersionResponse)
127+
assert isinstance(version, VersionGetResponse)
128+
129+
130+
def test_all_exports():
131+
"""Test that __all__ exports the expected items."""
132+
from replicate import model
133+
134+
expected_exports = {
135+
"Model",
136+
"Version",
137+
"Prediction",
138+
"ModelResponse",
139+
"VersionResponse",
140+
}
141+
142+
assert set(model.__all__) == expected_exports
143+
144+
# Verify all exported items are importable
145+
for name in model.__all__:
146+
assert hasattr(model, name)
147+
148+
149+
if TYPE_CHECKING:
150+
# Type checking test - ensure type annotations work correctly
151+
def type_annotation_test():
152+
from replicate.model import Model, Version, ModelResponse, VersionResponse
153+
154+
model: Model = Model("owner", "name") # pyright: ignore[reportUnusedVariable]
155+
version: Version # pyright: ignore[reportUnusedVariable]
156+
prediction: Prediction # pyright: ignore[reportUnusedVariable]
157+
model_response: ModelResponse # pyright: ignore[reportUnusedVariable]
158+
version_response: VersionResponse # pyright: ignore[reportUnusedVariable]
159+

0 commit comments

Comments
 (0)