Skip to content

Commit f345e79

Browse files
CopilotOhYee
andauthored
Fix config not forwarded to with_path in DataAPI HTTP methods
All HTTP methods (get, post, put, patch, delete and their async/file/video variants) in DataAPI were calling self.with_path(path, query=query) without forwarding the config parameter. This caused get_base_url() to use only self.config (which was None when SandboxClient() was created without config), ignoring any account_id provided at call time via the config argument. Fix: pass config=config to all self.with_path() calls so the merged config (self.config + call-time config) is used when constructing the base URL. Apply identical fix to the __data_api_async_template.py source template. Add regression tests in TestDataAPIConfigForwarding to verify that HTTP methods use call-time config for URL construction even when the instance was created without an account_id. Agent-Logs-Url: https://github.com/Serverless-Devs/agentrun-sdk-python/sessions/04987b1e-1f19-49af-8878-9513a91f6abf Co-authored-by: OhYee <[email protected]>
1 parent efd264e commit f345e79

File tree

3 files changed

+143
-24
lines changed

3 files changed

+143
-24
lines changed

agentrun/utils/__data_api_async_template.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ async def get_async(
435435
"""
436436
return await self._make_request_async(
437437
"GET",
438-
self.with_path(path, query=query),
438+
self.with_path(path, query=query, config=config),
439439
headers=headers,
440440
config=config,
441441
)
@@ -470,7 +470,7 @@ async def post_async(
470470

471471
return await self._make_request_async(
472472
"POST",
473-
self.with_path(path, query=query),
473+
self.with_path(path, query=query, config=config),
474474
data=data,
475475
headers=headers,
476476
config=config,
@@ -501,7 +501,7 @@ async def put_async(
501501
"""
502502
return await self._make_request_async(
503503
"PUT",
504-
self.with_path(path, query=query),
504+
self.with_path(path, query=query, config=config),
505505
data=data,
506506
headers=headers,
507507
config=config,
@@ -532,7 +532,7 @@ async def patch_async(
532532
"""
533533
return await self._make_request_async(
534534
"PATCH",
535-
self.with_path(path, query=query),
535+
self.with_path(path, query=query, config=config),
536536
data=data,
537537
headers=headers,
538538
config=config,
@@ -561,7 +561,7 @@ async def delete_async(
561561
"""
562562
return await self._make_request_async(
563563
"DELETE",
564-
self.with_path(path, query=query),
564+
self.with_path(path, query=query, config=config),
565565
headers=headers,
566566
config=config,
567567
)
@@ -601,7 +601,7 @@ async def post_file_async(
601601

602602
filename = os.path.basename(local_file_path)
603603

604-
url = self.with_path(path, query=query)
604+
url = self.with_path(path, query=query, config=config)
605605
req_headers = self.config.get_headers()
606606
req_headers.update(headers or {})
607607
# Apply authentication (may modify URL, headers, and query)
@@ -656,7 +656,7 @@ async def get_file_async(
656656
Examples:
657657
>>> await client.get_file_async("/files", save_path="/local/data.csv", query={"path": "/remote/file.csv"})
658658
"""
659-
url = self.with_path(path, query=query)
659+
url = self.with_path(path, query=query, config=config)
660660
req_headers = self.config.get_headers()
661661
req_headers.update(headers or {})
662662
# Apply authentication (may modify URL, headers, and query)
@@ -707,7 +707,7 @@ async def get_video_async(
707707
Examples:
708708
>>> await client.get_video_async("/videos", save_path="/local/video.mkv", query={"path": "/remote/video.mp4"})
709709
"""
710-
url = self.with_path(path, query=query)
710+
url = self.with_path(path, query=query, config=config)
711711
req_headers = self.config.get_headers()
712712
req_headers.update(headers or {})
713713
# Apply authentication (may modify URL, headers, and query)

agentrun/utils/data_api.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ async def get_async(
528528
"""
529529
return await self._make_request_async(
530530
"GET",
531-
self.with_path(path, query=query),
531+
self.with_path(path, query=query, config=config),
532532
headers=headers,
533533
config=config,
534534
)
@@ -561,7 +561,7 @@ def get(
561561
"""
562562
return self._make_request(
563563
"GET",
564-
self.with_path(path, query=query),
564+
self.with_path(path, query=query, config=config),
565565
headers=headers,
566566
config=config,
567567
)
@@ -596,7 +596,7 @@ async def post_async(
596596

597597
return await self._make_request_async(
598598
"POST",
599-
self.with_path(path, query=query),
599+
self.with_path(path, query=query, config=config),
600600
data=data,
601601
headers=headers,
602602
config=config,
@@ -632,7 +632,7 @@ def post(
632632

633633
return self._make_request(
634634
"POST",
635-
self.with_path(path, query=query),
635+
self.with_path(path, query=query, config=config),
636636
data=data,
637637
headers=headers,
638638
config=config,
@@ -663,7 +663,7 @@ async def put_async(
663663
"""
664664
return await self._make_request_async(
665665
"PUT",
666-
self.with_path(path, query=query),
666+
self.with_path(path, query=query, config=config),
667667
data=data,
668668
headers=headers,
669669
config=config,
@@ -694,7 +694,7 @@ def put(
694694
"""
695695
return self._make_request(
696696
"PUT",
697-
self.with_path(path, query=query),
697+
self.with_path(path, query=query, config=config),
698698
data=data,
699699
headers=headers,
700700
config=config,
@@ -725,7 +725,7 @@ async def patch_async(
725725
"""
726726
return await self._make_request_async(
727727
"PATCH",
728-
self.with_path(path, query=query),
728+
self.with_path(path, query=query, config=config),
729729
data=data,
730730
headers=headers,
731731
config=config,
@@ -756,7 +756,7 @@ def patch(
756756
"""
757757
return self._make_request(
758758
"PATCH",
759-
self.with_path(path, query=query),
759+
self.with_path(path, query=query, config=config),
760760
data=data,
761761
headers=headers,
762762
config=config,
@@ -785,7 +785,7 @@ async def delete_async(
785785
"""
786786
return await self._make_request_async(
787787
"DELETE",
788-
self.with_path(path, query=query),
788+
self.with_path(path, query=query, config=config),
789789
headers=headers,
790790
config=config,
791791
)
@@ -813,7 +813,7 @@ def delete(
813813
"""
814814
return self._make_request(
815815
"DELETE",
816-
self.with_path(path, query=query),
816+
self.with_path(path, query=query, config=config),
817817
headers=headers,
818818
config=config,
819819
)
@@ -853,7 +853,7 @@ async def post_file_async(
853853

854854
filename = os.path.basename(local_file_path)
855855

856-
url = self.with_path(path, query=query)
856+
url = self.with_path(path, query=query, config=config)
857857
req_headers = self.config.get_headers()
858858
req_headers.update(headers or {})
859859
# Apply authentication (may modify URL, headers, and query)
@@ -917,7 +917,7 @@ def post_file(
917917

918918
filename = os.path.basename(local_file_path)
919919

920-
url = self.with_path(path, query=query)
920+
url = self.with_path(path, query=query, config=config)
921921
req_headers = self.config.get_headers()
922922
req_headers.update(headers or {})
923923
# Apply authentication (may modify URL, headers, and query)
@@ -970,7 +970,7 @@ async def get_file_async(
970970
Examples:
971971
>>> await client.get_file_async("/files", save_path="/local/data.csv", query={"path": "/remote/file.csv"})
972972
"""
973-
url = self.with_path(path, query=query)
973+
url = self.with_path(path, query=query, config=config)
974974
req_headers = self.config.get_headers()
975975
req_headers.update(headers or {})
976976
# Apply authentication (may modify URL, headers, and query)
@@ -1021,7 +1021,7 @@ def get_file(
10211021
Examples:
10221022
>>> client.get_file("/files", save_path="/local/data.csv", query={"path": "/remote/file.csv"})
10231023
"""
1024-
url = self.with_path(path, query=query)
1024+
url = self.with_path(path, query=query, config=config)
10251025
req_headers = self.config.get_headers()
10261026
req_headers.update(headers or {})
10271027
# Apply authentication (may modify URL, headers, and query)
@@ -1070,7 +1070,7 @@ async def get_video_async(
10701070
Examples:
10711071
>>> await client.get_video_async("/videos", save_path="/local/video.mkv", query={"path": "/remote/video.mp4"})
10721072
"""
1073-
url = self.with_path(path, query=query)
1073+
url = self.with_path(path, query=query, config=config)
10741074
req_headers = self.config.get_headers()
10751075
req_headers.update(headers or {})
10761076
# Apply authentication (may modify URL, headers, and query)
@@ -1121,7 +1121,7 @@ def get_video(
11211121
Examples:
11221122
>>> client.get_video("/videos", save_path="/local/video.mkv", query={"path": "/remote/video.mp4"})
11231123
"""
1124-
url = self.with_path(path, query=query)
1124+
url = self.with_path(path, query=query, config=config)
11251125
req_headers = self.config.get_headers()
11261126
req_headers.update(headers or {})
11271127
# Apply authentication (may modify URL, headers, and query)

tests/unittests/utils/test_data_api.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,3 +1206,122 @@ def test_auth_with_sandbox_uses_ram_when_ak_sk_provided(
12061206
api.get_base_url().startswith("https://")
12071207
and "-ram." in api.get_base_url()
12081208
)
1209+
1210+
1211+
class TestDataAPIConfigForwarding:
1212+
"""测试 DataAPI 的 config 透传到 with_path 的行为(修复 issue #75)
1213+
1214+
当 DataAPI 实例初始化时没有提供 account_id(如通过 __get_client() 获取的无配置客户端),
1215+
但在调用 HTTP 方法时通过 config 参数传入 account_id,URL 应该使用该 account_id 构造。
1216+
"""
1217+
1218+
@respx.mock
1219+
def test_get_uses_call_time_config_for_url(self):
1220+
"""测试 get() 使用调用时传入的 config 构造 URL(不依赖实例 config 的 account_id)"""
1221+
# 实例创建时不提供 account_id(模拟 __get_client() 无配置的情况)
1222+
api = DataAPI(
1223+
resource_name="",
1224+
resource_type=ResourceType.Template,
1225+
config=None,
1226+
namespace="sandboxes",
1227+
)
1228+
1229+
# 调用时传入带有 account_id 的 config
1230+
call_config = Config(
1231+
account_id="call-time-account",
1232+
access_key_id="",
1233+
access_key_secret="",
1234+
region_id="cn-hangzhou",
1235+
)
1236+
1237+
respx.get(
1238+
"https://call-time-account.agentrun-data.cn-hangzhou.aliyuncs.com/sandboxes"
1239+
).mock(return_value=httpx.Response(200, json={"code": "SUCCESS"}))
1240+
1241+
result = api.get("/", config=call_config)
1242+
assert result == {"code": "SUCCESS"}
1243+
1244+
@respx.mock
1245+
def test_post_uses_call_time_config_for_url(self):
1246+
"""测试 post() 使用调用时传入的 config 构造 URL"""
1247+
api = DataAPI(
1248+
resource_name="",
1249+
resource_type=ResourceType.Template,
1250+
config=None,
1251+
namespace="sandboxes",
1252+
)
1253+
1254+
call_config = Config(
1255+
account_id="call-time-account",
1256+
access_key_id="",
1257+
access_key_secret="",
1258+
region_id="cn-hangzhou",
1259+
)
1260+
1261+
respx.post(
1262+
"https://call-time-account.agentrun-data.cn-hangzhou.aliyuncs.com/sandboxes"
1263+
).mock(return_value=httpx.Response(200, json={"code": "SUCCESS"}))
1264+
1265+
result = api.post("/", data={"key": "val"}, config=call_config)
1266+
assert result == {"code": "SUCCESS"}
1267+
1268+
@respx.mock
1269+
def test_delete_uses_call_time_config_for_url(self):
1270+
"""测试 delete() 使用调用时传入的 config 构造 URL"""
1271+
api = DataAPI(
1272+
resource_name="",
1273+
resource_type=ResourceType.Template,
1274+
config=None,
1275+
namespace="sandboxes",
1276+
)
1277+
1278+
call_config = Config(
1279+
account_id="call-time-account",
1280+
access_key_id="",
1281+
access_key_secret="",
1282+
region_id="cn-hangzhou",
1283+
)
1284+
1285+
respx.delete(
1286+
"https://call-time-account.agentrun-data.cn-hangzhou.aliyuncs.com/sandboxes"
1287+
).mock(return_value=httpx.Response(200, json={"code": "SUCCESS"}))
1288+
1289+
result = api.delete("/", config=call_config)
1290+
assert result == {"code": "SUCCESS"}
1291+
1292+
@respx.mock
1293+
@pytest.mark.asyncio
1294+
async def test_post_async_uses_call_time_config_for_url(self):
1295+
"""测试 post_async() 使用调用时传入的 config 构造 URL"""
1296+
api = DataAPI(
1297+
resource_name="",
1298+
resource_type=ResourceType.Template,
1299+
config=None,
1300+
namespace="sandboxes",
1301+
)
1302+
1303+
call_config = Config(
1304+
account_id="call-time-account",
1305+
access_key_id="",
1306+
access_key_secret="",
1307+
region_id="cn-hangzhou",
1308+
)
1309+
1310+
respx.post(
1311+
"https://call-time-account.agentrun-data.cn-hangzhou.aliyuncs.com/sandboxes"
1312+
).mock(return_value=httpx.Response(200, json={"code": "SUCCESS"}))
1313+
1314+
result = await api.post_async("/", data={"key": "val"}, config=call_config)
1315+
assert result == {"code": "SUCCESS"}
1316+
1317+
def test_no_account_id_without_call_time_config_raises(self):
1318+
"""测试未在实例 config 或调用 config 中设置 account_id 时抛出 ValueError"""
1319+
api = DataAPI(
1320+
resource_name="",
1321+
resource_type=ResourceType.Template,
1322+
config=None,
1323+
namespace="sandboxes",
1324+
)
1325+
1326+
with pytest.raises(ValueError, match="account id is not set"):
1327+
api.with_path("/")

0 commit comments

Comments
 (0)