Skip to content

Commit f925406

Browse files
fix(api_client): raise HTTPError instead of returning None on API errors
ApiClient methods now call response.raise_for_status() via _log_error instead of silently returning None/False/[] on HTTP errors. This allows callers to properly handle API failures through exception handling. Existing callers (CodeCarbonAPIOutput, _create_run, add_emission) already have try/except blocks that catch these exceptions gracefully. Relates to #820
1 parent e7e5186 commit f925406

2 files changed

Lines changed: 34 additions & 15 deletions

File tree

codecarbon/core/api_client.py

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ def check_auth(self):
8888
r = requests.get(url=url, timeout=2, headers=headers)
8989
if r.status_code != 200:
9090
self._log_error(url, {}, r)
91-
return None
9291
return r.json()
9392

9493
def get_list_organizations(self):
@@ -100,16 +99,13 @@ def get_list_organizations(self):
10099
r = requests.get(url=url, timeout=2, headers=headers)
101100
if r.status_code != 200:
102101
self._log_error(url, {}, r)
103-
return None
104102
return r.json()
105103

106104
def check_organization_exists(self, organization_name: str):
107105
"""
108106
Check if an organization exists
109107
"""
110108
organizations = self.get_list_organizations()
111-
if organizations is None:
112-
return False
113109
for organization in organizations:
114110
if organization["name"] == organization_name:
115111
return organization
@@ -131,7 +127,6 @@ def create_organization(self, organization: OrganizationCreate):
131127
r = requests.post(url=url, json=payload, timeout=2, headers=headers)
132128
if r.status_code != 201:
133129
self._log_error(url, payload, r)
134-
return None
135130
return r.json()
136131

137132
def get_organization(self, organization_id):
@@ -143,7 +138,6 @@ def get_organization(self, organization_id):
143138
r = requests.get(url=url, timeout=2, headers=headers)
144139
if r.status_code != 200:
145140
self._log_error(url, {}, r)
146-
return None
147141
return r.json()
148142

149143
def update_organization(self, organization: OrganizationCreate):
@@ -156,7 +150,6 @@ def update_organization(self, organization: OrganizationCreate):
156150
r = requests.patch(url=url, json=payload, timeout=2, headers=headers)
157151
if r.status_code != 200:
158152
self._log_error(url, payload, r)
159-
return None
160153
return r.json()
161154

162155
def list_projects_from_organization(self, organization_id):
@@ -168,7 +161,6 @@ def list_projects_from_organization(self, organization_id):
168161
r = requests.get(url=url, timeout=2, headers=headers)
169162
if r.status_code != 200:
170163
self._log_error(url, {}, r)
171-
return None
172164
return r.json()
173165

174166
def create_project(self, project: ProjectCreate):
@@ -181,7 +173,6 @@ def create_project(self, project: ProjectCreate):
181173
r = requests.post(url=url, json=payload, timeout=2, headers=headers)
182174
if r.status_code != 201:
183175
self._log_error(url, payload, r)
184-
return None
185176
return r.json()
186177

187178
def get_project(self, project_id):
@@ -193,7 +184,6 @@ def get_project(self, project_id):
193184
r = requests.get(url=url, timeout=2, headers=headers)
194185
if r.status_code != 200:
195186
self._log_error(url, {}, r)
196-
return None
197187
return r.json()
198188

199189
def add_emission(self, carbon_emission: dict):
@@ -235,7 +225,6 @@ def add_emission(self, carbon_emission: dict):
235225
r = requests.post(url=url, json=payload, timeout=2, headers=headers)
236226
if r.status_code != 201:
237227
self._log_error(url, payload, r)
238-
return False
239228
logger.debug(f"ApiClient - Successful upload emission {payload} to {url}")
240229
except Exception as e:
241230
logger.error(e, exc_info=True)
@@ -277,7 +266,6 @@ def _create_run(self, experiment_id: str):
277266
r = requests.post(url=url, json=payload, timeout=2, headers=headers)
278267
if r.status_code != 201:
279268
self._log_error(url, payload, r)
280-
return None
281269
self.run_id = r.json()["id"]
282270
logger.info(
283271
"ApiClient Successfully registered your run on the API.\n\n"
@@ -302,7 +290,6 @@ def list_experiments_from_project(self, project_id: str):
302290
r = requests.get(url=url, timeout=2, headers=headers)
303291
if r.status_code != 200:
304292
self._log_error(url, {}, r)
305-
return []
306293
return r.json()
307294

308295
def set_experiment(self, experiment_id: str):
@@ -322,7 +309,6 @@ def add_experiment(self, experiment: ExperimentCreate):
322309
r = requests.post(url=url, json=payload, timeout=2, headers=headers)
323310
if r.status_code != 201:
324311
self._log_error(url, payload, r)
325-
return None
326312
return r.json()
327313

328314
def get_experiment(self, experiment_id):
@@ -334,7 +320,6 @@ def get_experiment(self, experiment_id):
334320
r = requests.get(url=url, timeout=2, headers=headers)
335321
if r.status_code != 200:
336322
self._log_error(url, {}, r)
337-
return None
338323
return r.json()
339324

340325
def _log_error(self, url, payload, response):
@@ -347,6 +332,7 @@ def _log_error(self, url, payload, response):
347332
logger.error(
348333
f"ApiClient API return http code {response.status_code} and answer : {response.text}"
349334
)
335+
response.raise_for_status()
350336

351337
def close_experiment(self):
352338
"""

tests/test_api_call.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
import unittest
33
from uuid import uuid4
44

5+
import requests
56
import requests_mock
67

78
from codecarbon.core.api_client import ApiClient
9+
from codecarbon.core.schemas import ProjectCreate
810
from codecarbon.output import EmissionsData
911

1012
conf = {
@@ -106,3 +108,34 @@ def test_call_api(self):
106108
tracking_mode="Machine",
107109
)
108110
assert api.add_emission(dataclasses.asdict(carbon_emission))
111+
112+
@requests_mock.Mocker()
113+
def test_call_api_error(self, m):
114+
mock_url = "http://test.com"
115+
m.get(
116+
mock_url + "/organizations",
117+
json={"detail": "Internal Server Error"},
118+
status_code=500,
119+
)
120+
m.post(
121+
mock_url + "/projects",
122+
json={"detail": "Bad Request"},
123+
status_code=400,
124+
)
125+
api = ApiClient(
126+
experiment_id=None,
127+
endpoint_url=mock_url,
128+
api_key="api-key",
129+
conf={},
130+
create_run_automatically=False,
131+
)
132+
with self.assertRaises(requests.exceptions.HTTPError):
133+
api.get_list_organizations()
134+
with self.assertRaises(requests.exceptions.HTTPError):
135+
api.create_project(
136+
ProjectCreate(
137+
name="test_project",
138+
description="test_description",
139+
organization_id="test_org_id",
140+
)
141+
)

0 commit comments

Comments
 (0)