Skip to content

Commit cfcfb51

Browse files
authored
Log protobuf conversion issues (#101)
Ensure an error message with protobuf data is logged in case there are conversion problems.
2 parents f868ca7 + 5a03dba commit cfcfb51

File tree

2 files changed

+55
-9
lines changed

2 files changed

+55
-9
lines changed

RELEASE_NOTES.md

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,7 @@
1010

1111
## New Features
1212

13-
* Add trading-cli tool to interact with the trading API. Supports the following commands:
14-
* `list-day-ahead`: Listing day-ahead prices from the entsoe API.
15-
* `receive-trades`: Listing and streaming public trades for specified delivery periods. If no delivery start is given, starts streaming all new public trades.
16-
* `receive-orders`: Listing and streaming orders for specified delivery periods and gridpool IDs. If no delivery start is given, starts streaming all new orders for this gridpool ID.
17-
* `create-order`: Creating a limit order for a given price (in EUR/MWh) and quantity (in MW, sign determines market side).
18-
* `cancel-order`: Cancel individual orders for a gridpool.
19-
* `cancel-all-orders`: Cancels all orders of a gridpool.
20-
13+
* Use decorator for `from_pb` methods to ensure an error message with protobuf data is logged in case there are conversion problems.
2114

2215
<!-- Here goes the main new features and examples or instructions on how to use them -->
2316

src/frequenz/client/electricity_trading/_types.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
from dataclasses import dataclass
1313
from datetime import datetime, timedelta, timezone
1414
from decimal import Decimal
15-
from typing import Self
15+
from functools import wraps
16+
from typing import Callable, Concatenate, ParamSpec, Self, TypeVar
1617

1718
# pylint: disable=no-member
1819
from frequenz.api.common.v1.grid import delivery_area_pb2, delivery_duration_pb2
@@ -24,6 +25,35 @@
2425
_logger = logging.getLogger(__name__)
2526

2627

28+
T = TypeVar("T") # Generic type variable for class methods
29+
P = ParamSpec("P")
30+
31+
32+
def from_pb(
33+
func: Callable[Concatenate[type[T], P], T]
34+
) -> Callable[Concatenate[type[T], P], T]:
35+
"""Standardize from_pb methods like error handling with this decorator.
36+
37+
Args:
38+
func: A class method that converts a protobuf message into an object.
39+
40+
Returns:
41+
The wrapped function with standardized error handling.
42+
"""
43+
44+
@wraps(func)
45+
def wrapper(cls: type[T], /, *args: P.args, **kwargs: P.kwargs) -> T:
46+
try:
47+
return func(cls, *args, **kwargs)
48+
except Exception as e:
49+
_logger.error(
50+
"Error converting %s from protobuf (`%s`): %s", cls.__name__, *args, e
51+
)
52+
raise
53+
54+
return wrapper
55+
56+
2757
# From frequanz.api.common
2858
class Currency(enum.Enum):
2959
"""
@@ -56,6 +86,7 @@ class Currency(enum.Enum):
5686
SGD = price_pb2.Price.Currency.CURRENCY_SGD
5787

5888
@classmethod
89+
@from_pb
5990
def from_pb(cls, currency: price_pb2.Price.Currency.ValueType) -> "Currency":
6091
"""Convert a protobuf Currency value to Currency enum.
6192
@@ -91,6 +122,7 @@ class Price:
91122
"""Currency of the price."""
92123

93124
@classmethod
125+
@from_pb
94126
def from_pb(cls, price: price_pb2.Price) -> Self:
95127
"""Convert a protobuf Price to Price object.
96128
@@ -131,6 +163,7 @@ class Power:
131163
mw: Decimal
132164

133165
@classmethod
166+
@from_pb
134167
def from_pb(cls, power: power_pb2.Power) -> Self:
135168
"""Convert a protobuf Power to Power object.
136169
@@ -184,6 +217,7 @@ class EnergyMarketCodeType(enum.Enum):
184217
"""North American Electric Reliability Corporation identifiers."""
185218

186219
@classmethod
220+
@from_pb
187221
def from_pb(
188222
cls, energy_market_code_type: delivery_area_pb2.EnergyMarketCodeType.ValueType
189223
) -> "EnergyMarketCodeType":
@@ -229,6 +263,7 @@ class DeliveryArea:
229263
"""Type of code used for identifying the delivery area itself."""
230264

231265
@classmethod
266+
@from_pb
232267
def from_pb(cls, delivery_area: delivery_area_pb2.DeliveryArea) -> Self:
233268
"""Convert a protobuf DeliveryArea to DeliveryArea object.
234269
@@ -278,6 +313,7 @@ class DeliveryDuration(enum.Enum):
278313
"""1-hour contract duration."""
279314

280315
@classmethod
316+
@from_pb
281317
def from_pb(
282318
cls, delivery_duration: delivery_duration_pb2.DeliveryDuration.ValueType
283319
) -> "DeliveryDuration":
@@ -408,6 +444,7 @@ def __repr__(self) -> str:
408444
return self.__str__()
409445

410446
@classmethod
447+
@from_pb
411448
def from_pb(cls, delivery_period: delivery_duration_pb2.DeliveryPeriod) -> Self:
412449
"""Convert a protobuf DeliveryPeriod to DeliveryPeriod object.
413450
@@ -482,6 +519,7 @@ class OrderExecutionOption(enum.Enum):
482519
immediately will be cancelled."""
483520

484521
@classmethod
522+
@from_pb
485523
def from_pb(
486524
cls,
487525
order_execution_option: electricity_trading_pb2.OrderExecutionOption.ValueType,
@@ -547,6 +585,7 @@ class OrderType(enum.Enum):
547585
order book and has no market impact. (Not yet supported)."""
548586

549587
@classmethod
588+
@from_pb
550589
def from_pb(
551590
cls, order_type: electricity_trading_pb2.OrderType.ValueType
552591
) -> "OrderType":
@@ -586,6 +625,7 @@ class MarketSide(enum.Enum):
586625
"""Order to sell electricity, referred to as an 'ask' or 'offer' in the order book."""
587626

588627
@classmethod
628+
@from_pb
589629
def from_pb(
590630
cls, market_side: electricity_trading_pb2.MarketSide.ValueType
591631
) -> "MarketSide":
@@ -648,6 +688,7 @@ class OrderState(enum.Enum):
648688
could be due to certain conditions not yet being met."""
649689

650690
@classmethod
691+
@from_pb
651692
def from_pb(
652693
cls, order_state: electricity_trading_pb2.OrderState.ValueType
653694
) -> "OrderState":
@@ -711,6 +752,7 @@ class TradeState(enum.Enum):
711752
"""An approval has been requested."""
712753

713754
@classmethod
755+
@from_pb
714756
def from_pb(
715757
cls, trade_state: electricity_trading_pb2.TradeState.ValueType
716758
) -> "TradeState":
@@ -811,6 +853,7 @@ class StateReason(enum.Enum):
811853
"""A quote was partially executed."""
812854

813855
@classmethod
856+
@from_pb
814857
def from_pb(
815858
cls,
816859
state_reason: electricity_trading_pb2.OrderDetail.StateDetail.StateReason.ValueType,
@@ -864,6 +907,7 @@ class MarketActor(enum.Enum):
864907
"""The system was the actor."""
865908

866909
@classmethod
910+
@from_pb
867911
def from_pb(
868912
cls,
869913
market_actor: electricity_trading_pb2.OrderDetail.StateDetail.MarketActor.ValueType,
@@ -951,6 +995,7 @@ def __post_init__(self) -> None:
951995
self.valid_until = self.valid_until.astimezone(timezone.utc)
952996

953997
@classmethod
998+
@from_pb
954999
def from_pb(cls, order: electricity_trading_pb2.Order) -> Self:
9551000
"""Convert a protobuf Order to Order object.
9561001
@@ -1115,6 +1160,7 @@ def __post_init__(self) -> None:
11151160
self.execution_time = self.execution_time.astimezone(timezone.utc)
11161161

11171162
@classmethod
1163+
@from_pb
11181164
def from_pb(cls, trade: electricity_trading_pb2.Trade) -> Self:
11191165
"""Convert a protobuf Trade to Trade object.
11201166
@@ -1172,6 +1218,7 @@ class StateDetail:
11721218
"""Actor responsible for the current state."""
11731219

11741220
@classmethod
1221+
@from_pb
11751222
def from_pb(
11761223
cls, state_detail: electricity_trading_pb2.OrderDetail.StateDetail
11771224
) -> Self:
@@ -1252,6 +1299,7 @@ def __post_init__(self) -> None:
12521299
self.modification_time = self.modification_time.astimezone(timezone.utc)
12531300

12541301
@classmethod
1302+
@from_pb
12551303
def from_pb(cls, order_detail: electricity_trading_pb2.OrderDetail) -> Self:
12561304
"""Convert a protobuf OrderDetail to OrderDetail object.
12571305
@@ -1332,6 +1380,7 @@ def __post_init__(self) -> None:
13321380
self.execution_time = self.execution_time.astimezone(timezone.utc)
13331381

13341382
@classmethod
1383+
@from_pb
13351384
def from_pb(cls, public_trade: electricity_trading_pb2.PublicTrade) -> Self:
13361385
"""Convert a protobuf PublicTrade to PublicTrade object.
13371386
@@ -1430,6 +1479,7 @@ def __hash__(self) -> int:
14301479
)
14311480

14321481
@classmethod
1482+
@from_pb
14331483
def from_pb(
14341484
cls, gridpool_order_filter: electricity_trading_pb2.GridpoolOrderFilter
14351485
) -> Self:
@@ -1554,6 +1604,7 @@ def __hash__(self) -> int:
15541604
)
15551605

15561606
@classmethod
1607+
@from_pb
15571608
def from_pb(
15581609
cls, gridpool_trade_filter: electricity_trading_pb2.GridpoolTradeFilter
15591610
) -> "GridpoolTradeFilter":
@@ -1667,6 +1718,7 @@ def __hash__(self) -> int:
16671718
)
16681719

16691720
@classmethod
1721+
@from_pb
16701722
def from_pb(
16711723
cls, public_trade_filter: electricity_trading_pb2.PublicTradeFilter
16721724
) -> Self:
@@ -1779,6 +1831,7 @@ def __post_init__(self) -> None:
17791831
self.valid_until = self.valid_until.astimezone(timezone.utc)
17801832

17811833
@classmethod
1834+
@from_pb
17821835
def from_pb(
17831836
cls,
17841837
update_order: electricity_trading_pb2.UpdateGridpoolOrderRequest.UpdateOrder,

0 commit comments

Comments
 (0)