Skip to content

Commit 951d9c5

Browse files
zekeclaude
andauthored
feat: add get_path_url() function support for migration compatibility (#61)
* feat: add get_path_url() function support for migration compatibility Add support for get_path_url() function to maintain compatibility with old replicate-python client. This function extracts remote URLs from file output objects, enabling users to reuse URLs in model inputs. Key changes: - Export get_path_url from main module (__init__.py) - Add module-level function proxy in _module_client.py - Enhance FileOutput classes with __url__ attribute for compatibility - Add comprehensive test coverage for all supported object types - Maintain backward compatibility with same API as old client 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * refactor: simplify get_path_url import chain by removing intermediary Remove unnecessary lib/_get_path_url.py intermediary layer and import get_path_url directly from lib/_predictions_use.py in _module_client.py. This eliminates 32 lines of proxy code while maintaining identical functionality and API compatibility. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * fix: add bearer tokens to get_path_url tests to prevent auth errors The FileOutput and AsyncFileOutput classes require authenticated clients. Added TEST_TOKEN constant and provided bearer_token parameter to all client instantiations in tests to fix CI authentication failures. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * refactor: use cleaner import pattern for get_path_url re-export Replace lint suppression comments with explicit `import get_path_url as get_path_url` pattern to make the re-export intent clearer and avoid lint suppressions. Addresses PR feedback from RobertCraigle. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * refactor: import get_path_url directly in __init__.py instead of via module_client Move get_path_url import to follow the same pattern as other lib/ exports (FileOutput, Model, etc.) which are imported directly in __init__.py. The _module_client.py is specifically for resource proxies and module-level access patterns like replicate.models.list(), not for all top-level exports. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * refactor: remove get_path_url from module_client skip list Since get_path_url is now imported directly in __init__.py (not from _module_client), it should not be in the skip list for symbols that are imported later from _module_client. This ensures get_path_url gets proper __module__ attribution like other direct imports. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]>
1 parent b726483 commit 951d9c5

File tree

3 files changed

+115
-0
lines changed

3 files changed

+115
-0
lines changed

src/replicate/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
from .lib._models import Model as Model, Version as Version, ModelVersionIdentifier as ModelVersionIdentifier
4444
from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient
4545
from ._utils._logs import setup_logging as _setup_logging
46+
from .lib._predictions_use import get_path_url as get_path_url
4647

4748
__all__ = [
4849
"types",
@@ -89,6 +90,7 @@
8990
"Model",
9091
"Version",
9192
"ModelVersionIdentifier",
93+
"get_path_url",
9294
]
9395

9496
if not _t.TYPE_CHECKING:
@@ -104,6 +106,9 @@
104106
for __name in __all__:
105107
if not __name.startswith("__"):
106108
try:
109+
# Skip symbols that are imported later from _module_client
110+
if __name in ("run", "use"):
111+
continue
107112
__locals[__name].__module__ = "replicate"
108113
except (TypeError, AttributeError):
109114
# Some of our exported symbols are builtins which we can't set attributes for.

src/replicate/lib/_files.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ class FileOutput(httpx.SyncByteStream):
139139
def __init__(self, url: str, client: Replicate) -> None:
140140
self.url = url
141141
self._client = client
142+
# Add __url__ attribute for compatibility with get_path_url()
143+
self.__url__ = url
142144

143145
def read(self) -> bytes:
144146
if self.url.startswith("data:"):
@@ -184,6 +186,8 @@ class AsyncFileOutput(httpx.AsyncByteStream):
184186
def __init__(self, url: str, client: AsyncReplicate) -> None:
185187
self.url = url
186188
self._client = client
189+
# Add __url__ attribute for compatibility with get_path_url()
190+
self.__url__ = url
187191

188192
async def read(self) -> bytes:
189193
if self.url.startswith("data:"):

tests/lib/test_get_path_url.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
from pathlib import Path
2+
3+
import replicate
4+
from replicate.lib._files import FileOutput, AsyncFileOutput
5+
from replicate.lib._predictions_use import URLPath, get_path_url
6+
7+
# Test token for client instantiation
8+
TEST_TOKEN = "test-bearer-token"
9+
10+
11+
def test_get_path_url_with_urlpath():
12+
"""Test get_path_url returns the URL for URLPath instances."""
13+
url = "https://example.com/test.jpg"
14+
path_proxy = URLPath(url)
15+
16+
result = get_path_url(path_proxy)
17+
assert result == url
18+
19+
20+
def test_get_path_url_with_fileoutput():
21+
"""Test get_path_url returns the URL for FileOutput instances."""
22+
url = "https://example.com/test.jpg"
23+
file_output = FileOutput(url, replicate.Replicate(bearer_token=TEST_TOKEN))
24+
25+
result = get_path_url(file_output)
26+
assert result == url
27+
28+
29+
def test_get_path_url_with_async_fileoutput():
30+
"""Test get_path_url returns the URL for AsyncFileOutput instances."""
31+
url = "https://example.com/test.jpg"
32+
async_file_output = AsyncFileOutput(url, replicate.AsyncReplicate(bearer_token=TEST_TOKEN))
33+
34+
result = get_path_url(async_file_output)
35+
assert result == url
36+
37+
38+
def test_get_path_url_with_regular_path():
39+
"""Test get_path_url returns None for regular Path instances."""
40+
regular_path = Path("test.txt")
41+
42+
result = get_path_url(regular_path)
43+
assert result is None
44+
45+
46+
def test_get_path_url_with_object_without_target():
47+
"""Test get_path_url returns None for objects without __url__."""
48+
49+
# Test with a string
50+
result = get_path_url("not a path")
51+
assert result is None
52+
53+
# Test with a dict
54+
result = get_path_url({"key": "value"})
55+
assert result is None
56+
57+
# Test with None
58+
result = get_path_url(None)
59+
assert result is None
60+
61+
62+
def test_get_path_url_module_level_import():
63+
"""Test that get_path_url can be imported at module level."""
64+
from replicate import get_path_url as module_get_path_url
65+
66+
url = "https://example.com/test.jpg"
67+
file_output = FileOutput(url, replicate.Replicate(bearer_token=TEST_TOKEN))
68+
69+
result = module_get_path_url(file_output)
70+
assert result == url
71+
72+
73+
def test_get_path_url_direct_module_access():
74+
"""Test that get_path_url can be accessed directly from replicate module."""
75+
url = "https://example.com/test.jpg"
76+
file_output = FileOutput(url, replicate.Replicate(bearer_token=TEST_TOKEN))
77+
78+
result = replicate.get_path_url(file_output)
79+
assert result == url
80+
81+
82+
def test_fileoutput_has_url_attribute():
83+
"""Test that FileOutput instances have __url__ attribute."""
84+
url = "https://example.com/test.jpg"
85+
file_output = FileOutput(url, replicate.Replicate(bearer_token=TEST_TOKEN))
86+
87+
assert hasattr(file_output, "__url__")
88+
assert file_output.__url__ == url
89+
90+
91+
def test_async_fileoutput_has_url_attribute():
92+
"""Test that AsyncFileOutput instances have __url__ attribute."""
93+
url = "https://example.com/test.jpg"
94+
async_file_output = AsyncFileOutput(url, replicate.AsyncReplicate(bearer_token=TEST_TOKEN))
95+
96+
assert hasattr(async_file_output, "__url__")
97+
assert async_file_output.__url__ == url
98+
99+
100+
def test_urlpath_has_url_attribute():
101+
"""Test that URLPath instances have __url__ attribute."""
102+
url = "https://example.com/test.jpg"
103+
url_path = URLPath(url)
104+
105+
assert hasattr(url_path, "__url__")
106+
assert url_path.__url__ == url

0 commit comments

Comments
 (0)