99from pydantic import TypeAdapter
1010
1111from apify_shared .utils import ignore_docs
12+ from crawlee ._utils .context import ensure_context
1213
1314from apify ._models import ActorRun , PricingModel
1415from apify ._utils import docs_group
@@ -115,14 +116,15 @@ def __init__(self, configuration: Configuration, client: ApifyClientAsync) -> No
115116 self ._client = client
116117 self ._charging_log_dataset : Dataset | None = None
117118
118- self ._charging_state : dict [str , ChargingStateItem ] | None = None
119+ self ._charging_state : dict [str , ChargingStateItem ] = {}
119120 self ._pricing_info : dict [str , PricingInfoItem ] = {}
120121
121122 self ._not_ppe_warning_printed = False
123+ self .active = False
122124
123125 async def __aenter__ (self ) -> None :
124126 """Initialize the charging manager - this is called by the `Actor` class and shouldn't be invoked manually."""
125- self ._charging_state = {}
127+ self .active = True
126128
127129 if self ._is_at_home :
128130 # Running on the Apify platform - fetch pricing info for the current run.
@@ -169,12 +171,13 @@ async def __aexit__(
169171 exc_value : BaseException | None ,
170172 exc_traceback : TracebackType | None ,
171173 ) -> None :
172- pass
174+ if not self .active :
175+ raise RuntimeError ('Exiting an uninitialized ChargingManager' )
173176
174- async def charge (self , event_name : str , count : int = 1 ) -> ChargeResult :
175- if self ._charging_state is None :
176- raise RuntimeError ('Charging manager is not initialized' )
177+ self .active = False
177178
179+ @ensure_context
180+ async def charge (self , event_name : str , count : int = 1 ) -> ChargeResult :
178181 def calculate_chargeable () -> dict [str , int | None ]:
179182 """Calculate the maximum number of events of each type that can be charged within the current budget."""
180183 return {
@@ -265,19 +268,15 @@ def calculate_chargeable() -> dict[str, int | None]:
265268 chargeable_within_limit = calculate_chargeable (),
266269 )
267270
271+ @ensure_context
268272 def calculate_total_charged_amount (self ) -> Decimal :
269- if self ._charging_state is None :
270- raise RuntimeError ('Charging manager is not initialized' )
271-
272273 return sum (
273274 (item .total_charged_amount for item in self ._charging_state .values ()),
274275 start = Decimal (),
275276 )
276277
278+ @ensure_context
277279 def calculate_max_event_charge_count_within_limit (self , event_name : str ) -> int | None :
278- if self ._charging_state is None :
279- raise RuntimeError ('Charging manager is not initialized' )
280-
281280 pricing_info = self ._pricing_info .get (event_name )
282281
283282 if pricing_info is not None :
@@ -293,10 +292,8 @@ def calculate_max_event_charge_count_within_limit(self, event_name: str) -> int
293292 result = (self ._max_total_charge_usd - self .calculate_total_charged_amount ()) / price
294293 return math .floor (result ) if result .is_finite () else None
295294
295+ @ensure_context
296296 def get_pricing_info (self ) -> ActorPricingInfo :
297- if self ._charging_state is None :
298- raise RuntimeError ('Charging manager is not initialized' )
299-
300297 return ActorPricingInfo (
301298 pricing_model = self ._pricing_model ,
302299 is_pay_per_event = self ._pricing_model == 'PAY_PER_EVENT' ,
0 commit comments