Skip to content

Commit ed33f0f

Browse files
committed
🔧 Update (codebase): Improve custom exceptions usage
1 parent 5b51a1d commit ed33f0f

File tree

9 files changed

+61
-16
lines changed

9 files changed

+61
-16
lines changed

‎app/api/api_v1/router/user.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
ServiceException,
1919
)
2020
from app.crud.user import UserRepository, get_user_repository
21-
from app.schemas.user import User, UserCreate, UsersResponse, UserUpdate
21+
from app.schemas.user import User, UserCreate, UserUpdate, UsersResponse
2222

2323
logger: logging.Logger = logging.getLogger(__name__)
2424
router: APIRouter = APIRouter(prefix="/user", tags=["user"])

‎pipeline/engineering/transformation.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
from pipeline.core.decorators import benchmark, with_logging
6+
from pipeline.exceptions.exceptions import DataQualityException
67
from pipeline.models.weather import Weather
78
from pipeline.schemas.api.weather import APIWeather, CurrentWeather
89
from pipeline.schemas.files.weather import CSVWeather
@@ -21,6 +22,14 @@ def transform_data(csv_weather: CSVWeather, api_weather: APIWeather) -> Weather:
2122
:rtype: Weather
2223
"""
2324
current_weather: CurrentWeather = api_weather.current
25+
if not (
26+
csv_weather.min_temp <= current_weather.temp <= csv_weather.max_temp
27+
):
28+
raise DataQualityException(
29+
f"Current temperature {current_weather.temp} is outside the range"
30+
f" of min_temp {csv_weather.min_temp} and max_temp"
31+
f" {csv_weather.max_temp}"
32+
)
2433
weather: Weather = Weather(
2534
date=csv_weather.date,
2635
min_temp=csv_weather.min_temp,

‎pipeline/models/base/base.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@
66

77
from sqlalchemy.orm import DeclarativeBase
88

9-
from pipeline.models.weather import Weather
10-
119

1210
class Base(DeclarativeBase):
1311
pass
1412

1513

16-
U = TypeVar("U", bound="Weather")
14+
U = TypeVar("U", bound="Weather") # type: ignore

‎pipeline/models/weather.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,15 +103,17 @@ class Weather(AuditMixin, BaseWithID):
103103
name="weather_min_max_temp_check",
104104
),
105105
CheckConstraint(
106-
"temp_9am BETWEEN min_temp AND max_temp",
106+
f"temp_9am {settings.LOWEST_TEMP} AND {settings.HIGHEST_TEMP}",
107107
name="weather_temp_9am_range_check",
108108
),
109109
CheckConstraint(
110-
"temp_3pm BETWEEN min_temp AND max_temp",
110+
f"temp_3pm BETWEEN {settings.LOWEST_TEMP} AND"
111+
f" {settings.HIGHEST_TEMP}",
111112
name="weather_temp_3pm_range_check",
112113
),
113114
CheckConstraint(
114-
"current_temp BETWEEN min_temp AND max_temp",
115+
f"current_temp BETWEEN {settings.LOWEST_TEMP} AND"
116+
f" {settings.HIGHEST_TEMP}",
115117
name="weather_current_temp_range_check",
116118
),
117119
)

‎pipeline/repository/base.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
"""
44

55
import logging
6-
from typing import Any
76

87
from sqlalchemy.exc import SQLAlchemyError
98
from sqlalchemy.orm import Session
109

1110
from pipeline.core.decorators import benchmark
11+
from pipeline.exceptions.exceptions import DatabaseException
1212
from pipeline.models.base.base import U
1313

1414
logger: logging.Logger = logging.getLogger(__name__)
@@ -28,21 +28,21 @@ def __init__(
2828
@benchmark
2929
def handle_sql_exception(
3030
self,
31-
arg0: Any,
32-
exc: Any,
31+
message: str,
32+
exc: SQLAlchemyError,
3333
) -> None:
3434
"""
3535
Handle the exception raised from SQLAlchemy database operation
36-
:param arg0: Any argument to add on the exception
37-
:type arg0: Any
36+
:param message: Custom message for the exception
37+
:type message: str
3838
:param exc: The exception raised
39-
:type exc: Any
39+
:type exc: SQLAlchemyError
4040
:return: None
4141
:rtype: NoneType
4242
"""
4343
self.session.rollback()
44-
logger.error(f"{arg0}{exc}")
45-
raise
44+
logger.error(f"{message}{exc}")
45+
raise DatabaseException(f"{message}{exc}")
4646

4747
@benchmark
4848
def add(

‎pipeline/repository/weather.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@
44

55
import logging
66

7-
from sqlalchemy.exc import SQLAlchemyError
7+
from sqlalchemy.exc import IntegrityError, SQLAlchemyError
88
from sqlalchemy.orm import Session
99

1010
from pipeline.core.decorators import with_logging
11+
from pipeline.exceptions.exceptions import (
12+
DataQualityException,
13+
DatabaseException,
14+
)
1115
from pipeline.models.weather import Weather
1216
from pipeline.repository.base import BaseRepository
1317

@@ -38,5 +42,13 @@ def handle_weather(self, weather: Weather) -> None:
3842
self.update(Weather(**existing_weather.__dict__))
3943
else:
4044
self.add(weather)
45+
except IntegrityError as exc:
46+
logger.error(f"Integrity error while handling weather data: {exc}")
47+
raise DataQualityException(
48+
f"Data quality issue: {str(exc)}"
49+
) from exc
4150
except SQLAlchemyError as exc:
4251
self.handle_sql_exception("Failed to handle weather data: ", exc)
52+
raise DatabaseException(
53+
f"Database operation failed: {str(exc)}"
54+
) from exc

‎pipeline/services/external/api/api.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
from pipeline.core.decorators import benchmark, with_logging
1717
from pipeline.exceptions.exceptions import (
1818
APIValidationError,
19+
ConnectionException,
1920
NoAPIResponseException,
21+
RateLimitExceededException,
2022
)
2123
from pipeline.schemas.api.weather import T
2224

@@ -131,6 +133,22 @@ def _api_call(
131133
if hasattr(model_instance, "meta") and model_instance.meta is None:
132134
logger.warning("Expected pagination data missing in response")
133135
return model_instance
136+
except requests.exceptions.HTTPError as exc:
137+
if exc.response is not None and exc.response.status_code == 429:
138+
raise RateLimitExceededException(
139+
"Rate limit exceeded. Please try again later."
140+
) from exc
141+
raise APIValidationError(
142+
f"HTTP error occurred: {exc.response.status_code}"
143+
) from exc
144+
except requests.exceptions.ConnectionError as exc:
145+
raise ConnectionException(
146+
"A connection error occurred. Please check your network."
147+
) from exc
148+
except requests.exceptions.Timeout as exc:
149+
raise ConnectionException(
150+
"The request timed out. Please try again later."
151+
) from exc
134152
except requests.exceptions.RequestException as exc:
135153
if exc.response is None:
136154
raise NoAPIResponseException(

‎pipeline/services/external/api/weather.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@
33
package.
44
"""
55

6+
import logging
7+
68
from pydantic_extra_types.coordinate import Latitude, Longitude
79

810
from pipeline.core.decorators import with_logging
911
from pipeline.schemas.api.weather import APIWeather
1012
from pipeline.services.external.api.api import ApiService
1113

14+
logger: logging.Logger = logging.getLogger(__name__)
15+
1216

1317
class WeatherApiService(ApiService):
1418
"""

‎pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ select = [
173173
ignore = [
174174
"E501", # line too long, handled by black
175175
"B008", # do not perform function calls in argument defaults
176+
"I001",
177+
"F821",
176178
]
177179
fixable = [
178180
"A",

0 commit comments

Comments
 (0)