Skip to content

Commit db193c5

Browse files
committed
refactor: derive Pydantic model field aliases with to_camel
1 parent 10203bc commit db193c5

4 files changed

Lines changed: 74 additions & 34 deletions

File tree

src/apify/_charging.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
from dataclasses import dataclass
66
from datetime import UTC, datetime
77
from decimal import Decimal
8-
from typing import TYPE_CHECKING, Annotated, Literal, Protocol, TypedDict
8+
from typing import TYPE_CHECKING, Literal, Protocol, TypedDict
99

10-
from pydantic import BaseModel, ConfigDict, Field
10+
from pydantic import BaseModel, ConfigDict
11+
from pydantic.alias_generators import to_camel
1112

1213
import apify_client._models as _client_models
1314
from apify_client._models import ActorChargeEvent as ClientActorChargeEvent
@@ -52,22 +53,26 @@
5253
class _RelaxedPricingMetadata(BaseModel):
5354
"""Mixin relaxing the `CommonActorPricingInfo` metadata fields the platform env var omits."""
5455

55-
model_config = ConfigDict(populate_by_name=True, extra='allow')
56+
model_config = ConfigDict(populate_by_name=True, extra='allow', alias_generator=to_camel)
5657

57-
apify_margin_percentage: Annotated[float | None, Field(alias='apifyMarginPercentage')] = None
58-
created_at: Annotated[datetime | None, Field(alias='createdAt')] = None
59-
started_at: Annotated[datetime | None, Field(alias='startedAt')] = None
58+
apify_margin_percentage: float | None = None
59+
created_at: datetime | None = None
60+
started_at: datetime | None = None
6061

6162

6263
@docs_group('Charging')
6364
class ActorChargeEvent(ClientActorChargeEvent):
65+
model_config = ConfigDict(alias_generator=to_camel)
66+
6467
# `event_description` is required in apify-client but omitted from the env var.
65-
event_description: Annotated[str | None, Field(alias='eventDescription')] = None
68+
event_description: str | None = None
6669

6770

6871
@docs_group('Charging')
6972
class PricingPerEvent(ClientPricingPerEvent):
70-
actor_charge_events: Annotated[dict[str, ActorChargeEvent] | None, Field(alias='actorChargeEvents')] = None
73+
model_config = ConfigDict(alias_generator=to_camel)
74+
75+
actor_charge_events: dict[str, ActorChargeEvent] | None = None
7176

7277

7378
@docs_group('Charging')
@@ -77,20 +82,20 @@ class FreeActorPricingInfo(_RelaxedPricingMetadata, ClientFree):
7782

7883
@docs_group('Charging')
7984
class FlatPricePerMonthActorPricingInfo(_RelaxedPricingMetadata, ClientFlatPricePerMonth):
80-
trial_minutes: Annotated[int | None, Field(alias='trialMinutes')] = None
81-
price_per_unit_usd: Annotated[float | None, Field(alias='pricePerUnitUsd')] = None
85+
trial_minutes: int | None = None
86+
price_per_unit_usd: float | None = None
8287

8388

8489
@docs_group('Charging')
8590
class PricePerDatasetItemActorPricingInfo(_RelaxedPricingMetadata, ClientPricePerDatasetItem):
86-
unit_name: Annotated[str | None, Field(alias='unitName')] = None
91+
unit_name: str | None = None
8792
# `price_per_unit_usd` is already optional in apify-client - inherited.
8893

8994

9095
@docs_group('Charging')
9196
class PayPerEventActorPricingInfo(_RelaxedPricingMetadata, ClientPayPerEvent):
9297
# Re-typed to the relaxed element so an omitted `eventDescription` validates; the field stays required.
93-
pricing_per_event: Annotated[PricingPerEvent, Field(alias='pricingPerEvent')]
98+
pricing_per_event: PricingPerEvent
9499

95100

96101
ActorPricingInfoModel = ClientFree | ClientFlatPricePerMonth | ClientPricePerDatasetItem | ClientPayPerEvent

src/apify/events/_types.py

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
from datetime import datetime
44
from typing import Annotated, Any, Literal
55

6-
from pydantic import BaseModel, Field
6+
from pydantic import BaseModel, ConfigDict, Field
7+
from pydantic.alias_generators import to_camel
78

89
from crawlee.events._types import (
910
Event,
@@ -27,14 +28,16 @@
2728

2829
@docs_group('Event data')
2930
class SystemInfoEventData(BaseModel):
30-
mem_avg_bytes: Annotated[float, Field(alias='memAvgBytes')]
31-
mem_current_bytes: Annotated[float, Field(alias='memCurrentBytes')]
32-
mem_max_bytes: Annotated[float, Field(alias='memMaxBytes')]
33-
cpu_avg_usage: Annotated[float, Field(alias='cpuAvgUsage')]
34-
cpu_max_usage: Annotated[float, Field(alias='cpuMaxUsage')]
35-
cpu_current_usage: Annotated[float, Field(alias='cpuCurrentUsage')]
36-
is_cpu_overloaded: Annotated[bool, Field(alias='isCpuOverloaded')]
37-
created_at: Annotated[datetime, Field(alias='createdAt')]
31+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
32+
33+
mem_avg_bytes: float
34+
mem_current_bytes: float
35+
mem_max_bytes: float
36+
cpu_avg_usage: float
37+
cpu_max_usage: float
38+
cpu_current_usage: float
39+
is_cpu_overloaded: bool
40+
created_at: datetime
3841

3942
def to_crawlee_format(self, dedicated_cpus: float) -> EventSystemInfoData:
4043
return EventSystemInfoData.model_validate(
@@ -54,36 +57,48 @@ def to_crawlee_format(self, dedicated_cpus: float) -> EventSystemInfoData:
5457

5558
@docs_group('Events')
5659
class PersistStateEvent(BaseModel):
60+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
61+
5762
name: Literal[Event.PERSIST_STATE]
5863
data: Annotated[EventPersistStateData, Field(default_factory=lambda: EventPersistStateData(is_migrating=False))]
5964

6065

6166
@docs_group('Events')
6267
class SystemInfoEvent(BaseModel):
68+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
69+
6370
name: Literal[Event.SYSTEM_INFO]
6471
data: SystemInfoEventData
6572

6673

6774
@docs_group('Events')
6875
class MigratingEvent(BaseModel):
76+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
77+
6978
name: Literal[Event.MIGRATING]
7079
data: Annotated[EventMigratingData, Field(default_factory=EventMigratingData)]
7180

7281

7382
@docs_group('Events')
7483
class AbortingEvent(BaseModel):
84+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
85+
7586
name: Literal[Event.ABORTING]
7687
data: Annotated[EventAbortingData, Field(default_factory=EventAbortingData)]
7788

7889

7990
@docs_group('Events')
8091
class ExitEvent(BaseModel):
92+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
93+
8194
name: Literal[Event.EXIT]
8295
data: Annotated[EventExitData, Field(default_factory=EventExitData)]
8396

8497

8598
@docs_group('Events')
8699
class EventWithoutData(BaseModel):
100+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
101+
87102
name: Literal[
88103
Event.SESSION_RETIRED,
89104
Event.BROWSER_LAUNCHED,
@@ -97,12 +112,16 @@ class EventWithoutData(BaseModel):
97112

98113
@docs_group('Events')
99114
class DeprecatedEvent(BaseModel):
115+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
116+
100117
name: Literal['cpuInfo']
101118
data: Annotated[dict[str, Any], Field(default_factory=dict)]
102119

103120

104121
@docs_group('Events')
105122
class UnknownEvent(BaseModel):
123+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
124+
106125
name: str
107126
data: Annotated[dict[str, Any], Field(default_factory=dict)]
108127

src/apify/request_loaders/_apify_request_list.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
from itertools import chain
66
from typing import Annotated, Any
77

8-
from pydantic import BaseModel, Field, TypeAdapter
8+
from pydantic import BaseModel, ConfigDict, Field, TypeAdapter
9+
from pydantic.alias_generators import to_camel
910

1011
from crawlee._types import HttpMethod
1112
from crawlee.http_clients import HttpClient, ImpitHttpClient
@@ -20,14 +21,16 @@
2021

2122

2223
class _RequestDetails(BaseModel):
24+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
25+
2326
method: HttpMethod = 'GET'
2427
payload: str = ''
2528
headers: Annotated[dict[str, str], Field(default_factory=dict)]
26-
user_data: Annotated[dict[str, str], Field(default_factory=dict, alias='userData')]
29+
user_data: Annotated[dict[str, str], Field(default_factory=dict)]
2730

2831

2932
class _RequestsFromUrlInput(_RequestDetails):
30-
requests_from_url: str = Field(alias='requestsFromUrl')
33+
requests_from_url: str
3134

3235

3336
class _SimpleUrlInput(_RequestDetails):

src/apify/storage_clients/_apify/_models.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
from datetime import datetime, timedelta
44
from typing import TYPE_CHECKING, Annotated
55

6-
from pydantic import BaseModel, ConfigDict, Field
6+
from pydantic import AliasChoices, BaseModel, ConfigDict, Field
7+
from pydantic.alias_generators import to_camel
78

89
from apify_client._models import RequestQueueStats
910
from crawlee.storage_clients.models import KeyValueStoreMetadata, RequestQueueMetadata
@@ -22,7 +23,9 @@ class ApifyKeyValueStoreMetadata(KeyValueStoreMetadata):
2223
Includes additional Apify-specific fields.
2324
"""
2425

25-
url_signing_secret_key: Annotated[str | None, Field(alias='urlSigningSecretKey', default=None)]
26+
model_config = ConfigDict(alias_generator=to_camel)
27+
28+
url_signing_secret_key: str | None = None
2629
"""The secret key used for signing URLs for secure access to key-value store records."""
2730

2831

@@ -34,24 +37,30 @@ class RequestQueueHead(BaseModel):
3437
including metadata about the queue's state and lock information for the requests.
3538
"""
3639

37-
model_config = ConfigDict(populate_by_name=True, extra='allow')
40+
model_config = ConfigDict(populate_by_name=True, extra='allow', alias_generator=to_camel)
3841

39-
limit: Annotated[int | None, Field(alias='limit', default=None)]
42+
limit: int | None = None
4043
"""The maximum number of requests that were requested from the queue."""
4144

42-
had_multiple_clients: Annotated[bool, Field(alias='hadMultipleClients', default=False)]
45+
had_multiple_clients: bool = False
4346
"""Indicates whether the queue has been accessed by multiple clients (consumers)."""
4447

45-
queue_modified_at: Annotated[datetime, Field(alias='queueModifiedAt')]
48+
queue_modified_at: datetime
4649
"""The timestamp when the queue was last modified."""
4750

48-
lock_time: Annotated[timedelta | None, Field(alias='lockSecs', default=None)]
51+
# Accept both `lockSecs` (the lock duration in seconds, the key the API actually sends) and the camelCase
52+
# `lockTime` that `to_camel` derives from the field name. Serialization keeps emitting `lockSecs` for backward
53+
# compatibility. The field is named `lock_time` because Pydantic parses the value into a `timedelta`.
54+
lock_time: Annotated[
55+
timedelta | None,
56+
Field(validation_alias=AliasChoices('lockSecs', 'lockTime'), serialization_alias='lockSecs'),
57+
] = None
4958
"""The duration for which the returned requests are locked and cannot be processed by other clients."""
5059

51-
queue_has_locked_requests: Annotated[bool | None, Field(alias='queueHasLockedRequests', default=False)]
60+
queue_has_locked_requests: bool | None = False
5261
"""Indicates whether the queue contains any locked requests."""
5362

54-
items: Annotated[list[Request], Field(alias='items', default_factory=list[Request])]
63+
items: Annotated[list[Request], Field(default_factory=list[Request])]
5564
"""The list of request objects retrieved from the beginning of the queue."""
5665

5766
@classmethod
@@ -77,6 +86,8 @@ class CachedRequest(BaseModel):
7786
Only internal structure.
7887
"""
7988

89+
model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
90+
8091
id: str
8192
"""Id of the request."""
8293

@@ -91,5 +102,7 @@ class CachedRequest(BaseModel):
91102

92103

93104
class ApifyRequestQueueMetadata(RequestQueueMetadata):
94-
stats: Annotated[RequestQueueStats, Field(alias='stats', default_factory=RequestQueueStats)]
105+
model_config = ConfigDict(alias_generator=to_camel)
106+
107+
stats: Annotated[RequestQueueStats, Field(default_factory=RequestQueueStats)]
95108
"""Additional statistics about the request queue."""

0 commit comments

Comments
 (0)