diff --git a/agentrun/utils/__data_api_async_template.py b/agentrun/utils/__data_api_async_template.py index 00b6cea..2b5240d 100644 --- a/agentrun/utils/__data_api_async_template.py +++ b/agentrun/utils/__data_api_async_template.py @@ -435,7 +435,7 @@ async def get_async( """ return await self._make_request_async( "GET", - self.with_path(path, query=query), + self.with_path(path, query=query, config=config), headers=headers, config=config, ) @@ -470,7 +470,7 @@ async def post_async( return await self._make_request_async( "POST", - self.with_path(path, query=query), + self.with_path(path, query=query, config=config), data=data, headers=headers, config=config, @@ -501,7 +501,7 @@ async def put_async( """ return await self._make_request_async( "PUT", - self.with_path(path, query=query), + self.with_path(path, query=query, config=config), data=data, headers=headers, config=config, @@ -532,7 +532,7 @@ async def patch_async( """ return await self._make_request_async( "PATCH", - self.with_path(path, query=query), + self.with_path(path, query=query, config=config), data=data, headers=headers, config=config, @@ -561,7 +561,7 @@ async def delete_async( """ return await self._make_request_async( "DELETE", - self.with_path(path, query=query), + self.with_path(path, query=query, config=config), headers=headers, config=config, ) @@ -601,7 +601,7 @@ async def post_file_async( filename = os.path.basename(local_file_path) - url = self.with_path(path, query=query) + url = self.with_path(path, query=query, config=config) req_headers = self.config.get_headers() req_headers.update(headers or {}) # Apply authentication (may modify URL, headers, and query) @@ -656,7 +656,7 @@ async def get_file_async( Examples: >>> await client.get_file_async("/files", save_path="/local/data.csv", query={"path": "/remote/file.csv"}) """ - url = self.with_path(path, query=query) + url = self.with_path(path, query=query, config=config) req_headers = self.config.get_headers() req_headers.update(headers or {}) # Apply authentication (may modify URL, headers, and query) @@ -707,7 +707,7 @@ async def get_video_async( Examples: >>> await client.get_video_async("/videos", save_path="/local/video.mkv", query={"path": "/remote/video.mp4"}) """ - url = self.with_path(path, query=query) + url = self.with_path(path, query=query, config=config) req_headers = self.config.get_headers() req_headers.update(headers or {}) # Apply authentication (may modify URL, headers, and query) diff --git a/agentrun/utils/data_api.py b/agentrun/utils/data_api.py index fd41183..f522f87 100644 --- a/agentrun/utils/data_api.py +++ b/agentrun/utils/data_api.py @@ -528,7 +528,7 @@ async def get_async( """ return await self._make_request_async( "GET", - self.with_path(path, query=query), + self.with_path(path, query=query, config=config), headers=headers, config=config, ) @@ -561,7 +561,7 @@ def get( """ return self._make_request( "GET", - self.with_path(path, query=query), + self.with_path(path, query=query, config=config), headers=headers, config=config, ) @@ -596,7 +596,7 @@ async def post_async( return await self._make_request_async( "POST", - self.with_path(path, query=query), + self.with_path(path, query=query, config=config), data=data, headers=headers, config=config, @@ -632,7 +632,7 @@ def post( return self._make_request( "POST", - self.with_path(path, query=query), + self.with_path(path, query=query, config=config), data=data, headers=headers, config=config, @@ -663,7 +663,7 @@ async def put_async( """ return await self._make_request_async( "PUT", - self.with_path(path, query=query), + self.with_path(path, query=query, config=config), data=data, headers=headers, config=config, @@ -694,7 +694,7 @@ def put( """ return self._make_request( "PUT", - self.with_path(path, query=query), + self.with_path(path, query=query, config=config), data=data, headers=headers, config=config, @@ -725,7 +725,7 @@ async def patch_async( """ return await self._make_request_async( "PATCH", - self.with_path(path, query=query), + self.with_path(path, query=query, config=config), data=data, headers=headers, config=config, @@ -756,7 +756,7 @@ def patch( """ return self._make_request( "PATCH", - self.with_path(path, query=query), + self.with_path(path, query=query, config=config), data=data, headers=headers, config=config, @@ -785,7 +785,7 @@ async def delete_async( """ return await self._make_request_async( "DELETE", - self.with_path(path, query=query), + self.with_path(path, query=query, config=config), headers=headers, config=config, ) @@ -813,7 +813,7 @@ def delete( """ return self._make_request( "DELETE", - self.with_path(path, query=query), + self.with_path(path, query=query, config=config), headers=headers, config=config, ) @@ -853,7 +853,7 @@ async def post_file_async( filename = os.path.basename(local_file_path) - url = self.with_path(path, query=query) + url = self.with_path(path, query=query, config=config) req_headers = self.config.get_headers() req_headers.update(headers or {}) # Apply authentication (may modify URL, headers, and query) @@ -917,7 +917,7 @@ def post_file( filename = os.path.basename(local_file_path) - url = self.with_path(path, query=query) + url = self.with_path(path, query=query, config=config) req_headers = self.config.get_headers() req_headers.update(headers or {}) # Apply authentication (may modify URL, headers, and query) @@ -970,7 +970,7 @@ async def get_file_async( Examples: >>> await client.get_file_async("/files", save_path="/local/data.csv", query={"path": "/remote/file.csv"}) """ - url = self.with_path(path, query=query) + url = self.with_path(path, query=query, config=config) req_headers = self.config.get_headers() req_headers.update(headers or {}) # Apply authentication (may modify URL, headers, and query) @@ -1021,7 +1021,7 @@ def get_file( Examples: >>> client.get_file("/files", save_path="/local/data.csv", query={"path": "/remote/file.csv"}) """ - url = self.with_path(path, query=query) + url = self.with_path(path, query=query, config=config) req_headers = self.config.get_headers() req_headers.update(headers or {}) # Apply authentication (may modify URL, headers, and query) @@ -1070,7 +1070,7 @@ async def get_video_async( Examples: >>> await client.get_video_async("/videos", save_path="/local/video.mkv", query={"path": "/remote/video.mp4"}) """ - url = self.with_path(path, query=query) + url = self.with_path(path, query=query, config=config) req_headers = self.config.get_headers() req_headers.update(headers or {}) # Apply authentication (may modify URL, headers, and query) @@ -1121,7 +1121,7 @@ def get_video( Examples: >>> client.get_video("/videos", save_path="/local/video.mkv", query={"path": "/remote/video.mp4"}) """ - url = self.with_path(path, query=query) + url = self.with_path(path, query=query, config=config) req_headers = self.config.get_headers() req_headers.update(headers or {}) # Apply authentication (may modify URL, headers, and query) diff --git a/tests/unittests/utils/test_data_api.py b/tests/unittests/utils/test_data_api.py index 9534147..8994c2e 100644 --- a/tests/unittests/utils/test_data_api.py +++ b/tests/unittests/utils/test_data_api.py @@ -1206,3 +1206,122 @@ def test_auth_with_sandbox_uses_ram_when_ak_sk_provided( api.get_base_url().startswith("https://") and "-ram." in api.get_base_url() ) + + +class TestDataAPIConfigForwarding: + """测试 DataAPI 的 config 透传到 with_path 的行为(修复 issue #75) + + 当 DataAPI 实例初始化时没有提供 account_id(如通过 __get_client() 获取的无配置客户端), + 但在调用 HTTP 方法时通过 config 参数传入 account_id,URL 应该使用该 account_id 构造。 + """ + + @respx.mock + def test_get_uses_call_time_config_for_url(self): + """测试 get() 使用调用时传入的 config 构造 URL(不依赖实例 config 的 account_id)""" + # 实例创建时不提供 account_id(模拟 __get_client() 无配置的情况) + api = DataAPI( + resource_name="", + resource_type=ResourceType.Template, + config=None, + namespace="sandboxes", + ) + + # 调用时传入带有 account_id 的 config + call_config = Config( + account_id="call-time-account", + access_key_id="", + access_key_secret="", + region_id="cn-hangzhou", + ) + + respx.get( + "https://call-time-account.agentrun-data.cn-hangzhou.aliyuncs.com/sandboxes" + ).mock(return_value=httpx.Response(200, json={"code": "SUCCESS"})) + + result = api.get("/", config=call_config) + assert result == {"code": "SUCCESS"} + + @respx.mock + def test_post_uses_call_time_config_for_url(self): + """测试 post() 使用调用时传入的 config 构造 URL""" + api = DataAPI( + resource_name="", + resource_type=ResourceType.Template, + config=None, + namespace="sandboxes", + ) + + call_config = Config( + account_id="call-time-account", + access_key_id="", + access_key_secret="", + region_id="cn-hangzhou", + ) + + respx.post( + "https://call-time-account.agentrun-data.cn-hangzhou.aliyuncs.com/sandboxes" + ).mock(return_value=httpx.Response(200, json={"code": "SUCCESS"})) + + result = api.post("/", data={"key": "val"}, config=call_config) + assert result == {"code": "SUCCESS"} + + @respx.mock + def test_delete_uses_call_time_config_for_url(self): + """测试 delete() 使用调用时传入的 config 构造 URL""" + api = DataAPI( + resource_name="", + resource_type=ResourceType.Template, + config=None, + namespace="sandboxes", + ) + + call_config = Config( + account_id="call-time-account", + access_key_id="", + access_key_secret="", + region_id="cn-hangzhou", + ) + + respx.delete( + "https://call-time-account.agentrun-data.cn-hangzhou.aliyuncs.com/sandboxes" + ).mock(return_value=httpx.Response(200, json={"code": "SUCCESS"})) + + result = api.delete("/", config=call_config) + assert result == {"code": "SUCCESS"} + + @respx.mock + @pytest.mark.asyncio + async def test_post_async_uses_call_time_config_for_url(self): + """测试 post_async() 使用调用时传入的 config 构造 URL""" + api = DataAPI( + resource_name="", + resource_type=ResourceType.Template, + config=None, + namespace="sandboxes", + ) + + call_config = Config( + account_id="call-time-account", + access_key_id="", + access_key_secret="", + region_id="cn-hangzhou", + ) + + respx.post( + "https://call-time-account.agentrun-data.cn-hangzhou.aliyuncs.com/sandboxes" + ).mock(return_value=httpx.Response(200, json={"code": "SUCCESS"})) + + result = await api.post_async("/", data={"key": "val"}, config=call_config) + assert result == {"code": "SUCCESS"} + + def test_no_account_id_without_call_time_config_raises(self): + """测试未在实例 config 或调用 config 中设置 account_id 时抛出 ValueError""" + api = DataAPI( + resource_name="", + resource_type=ResourceType.Template, + config=None, + namespace="sandboxes", + ) + + with pytest.raises(ValueError, match="account id is not set"): + api.with_path("/")