Skip to content
This repository has been archived by the owner on Oct 3, 2024. It is now read-only.

Commit

Permalink
Use Pydantic 2.0 (#236)
Browse files Browse the repository at this point in the history
* chore(deps): update pydantic requirement from ^1.8.0 to >=1.8,<3.0

Updates the requirements on [pydantic](https://github.com/pydantic/pydantic) to permit the latest version.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md)
- [Commits](pydantic/pydantic@v1.8...v2.8.2)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>

* Use pydantic 2.0

* Update

* Update

* Run bump-pydantic

* Fix tests

* Update

* Fix formatting

* Fix linting

---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Lev Vereshchagin <[email protected]>
  • Loading branch information
dependabot[bot] and vrslev authored Aug 1, 2024
1 parent 2b269c2 commit 1f0bf76
Show file tree
Hide file tree
Showing 12 changed files with 82 additions and 84 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ include = ["src/ikea_api/py.typed"]
python = "^3.8"
typing-extensions = {version = "^4.0.0", python = "<3.10"}
requests = {version = "*", optional = true}
pydantic = {version = "^1.8.0", optional = true}
pydantic = {version = ">=2.0,<3.0", optional = true}
httpx = {version = "^0.23", optional = true}

[tool.poetry.dev-dependencies]
Expand Down
14 changes: 7 additions & 7 deletions src/ikea_api/wrappers/parsers/ingka_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,17 @@ class ReferenceMeasurements(BaseModel):


class Measurements(BaseModel):
referenceMeasurements: Optional[List[ReferenceMeasurements]]
referenceMeasurements: Optional[List[ReferenceMeasurements]] = None


class LocalisedCommunication(BaseModel):
languageCode: str
packageMeasurements: Optional[List[PackageMeasurement]]
media: Optional[List[Media]]
packageMeasurements: Optional[List[PackageMeasurement]] = None
media: Optional[List[Media]] = None
productName: str
productType: ProductType
validDesign: Optional[ValidDesign]
measurements: Optional[Measurements]
validDesign: Optional[ValidDesign] = None
measurements: Optional[Measurements] = None


class ChildItem(BaseModel):
Expand All @@ -69,7 +69,7 @@ class ChildItem(BaseModel):
class ResponseIngkaItem(BaseModel):
itemKey: ItemKey
localisedCommunications: List[LocalisedCommunication]
childItems: Optional[List[ChildItem]]
childItems: Optional[List[ChildItem]] = None


class ResponseIngkaItems(BaseModel):
Expand Down Expand Up @@ -178,6 +178,6 @@ def parse_item(constants: Constants, item: ResponseIngkaItem) -> types.IngkaItem
def parse_ingka_items(
constants: Constants, response: dict[str, Any]
) -> Iterable[types.IngkaItem]:
parsed_resp = ResponseIngkaItems(**response)
parsed_resp = ResponseIngkaItems.model_validate(response)
for item in parsed_resp.data:
yield parse_item(constants, item)
28 changes: 13 additions & 15 deletions src/ikea_api/wrappers/parsers/item_base.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
from typing import Any, Literal

from ikea_api.utils import parse_item_codes
from pydantic import BeforeValidator
from typing_extensions import Annotated

from ikea_api.utils import parse_item_codes

class ItemCode(str):
@classmethod
def __get_validators__(cls):
yield cls.validate

@classmethod
def validate(cls, v: Any):
if isinstance(v, int):
v = str(v)
if isinstance(v, str):
parsed_item_codes = parse_item_codes(v)
if len(parsed_item_codes) != 1:
raise ValueError("invalid item code format")
return parsed_item_codes[0]
raise TypeError("string required")
def validate_item_code(value: Any) -> str:
if isinstance(value, int):
value = str(value)
if isinstance(value, str):
parsed_item_codes = parse_item_codes(value)
if len(parsed_item_codes) != 1:
raise ValueError("invalid item code format")
return parsed_item_codes[0]
raise TypeError("string required")


ItemCode = Annotated[str, BeforeValidator(validate_item_code)]
ItemType = Literal["ART", "SPR"]


Expand Down
51 changes: 23 additions & 28 deletions src/ikea_api/wrappers/parsers/order_capture.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from __future__ import annotations

from datetime import datetime
from datetime import date, datetime
from typing import Any, List, Optional

from pydantic import BaseModel, validator # pyright: ignore[reportUnknownVariableType]
from pydantic import BaseModel, BeforeValidator
from typing_extensions import Annotated

from ikea_api.constants import Constants
from ikea_api.utils import translate_from_dict
Expand Down Expand Up @@ -53,17 +54,11 @@ class EarliestPossibleSlot(BaseModel):


class TimeWindows(BaseModel):
earliestPossibleSlot: Optional[EarliestPossibleSlot]
earliestPossibleSlot: Optional[EarliestPossibleSlot] = None


class SelectableInfo(BaseModel):
selectable: bool

@validator(
"selectable", pre=True
) # pyright: ignore[reportUntypedFunctionDecorator]
def validate_selectable(cls, v: Any) -> Any:
return v == "YES"
selectable: Annotated[bool, BeforeValidator(lambda value: value == "YES")]


class Metadata(BaseModel):
Expand All @@ -75,13 +70,13 @@ class UnavailableItem(BaseModel):
availableQuantity: int


def get_date(deliveries: list[HomeDelivery] | None) -> datetime | None:
def get_date(deliveries: list[HomeDelivery] | None) -> date | None:
if not deliveries:
return

for delivery in deliveries:
if delivery.timeWindows and delivery.timeWindows.earliestPossibleSlot:
return delivery.timeWindows.earliestPossibleSlot.fromDateTime
return delivery.timeWindows.earliestPossibleSlot.fromDateTime.date()


def get_type(
Expand Down Expand Up @@ -121,7 +116,7 @@ def get_unavailable_items(

class HomeDelivery(BaseModel):
type: str
timeWindows: Optional[TimeWindows]
timeWindows: Optional[TimeWindows] = None


class HomePossibleDeliveries(BaseModel):
Expand All @@ -131,24 +126,24 @@ class HomePossibleDeliveries(BaseModel):
class HomeDeliveryService(BaseModel):
metadata: Metadata
fulfillmentMethodType: str
solution: Optional[str]
solutionPrice: Optional[SolutionPrice]
possibleDeliveries: Optional[HomePossibleDeliveries]
unavailableItems: Optional[List[UnavailableItem]]
solution: Optional[str] = None
solutionPrice: Optional[SolutionPrice] = None
possibleDeliveries: Optional[HomePossibleDeliveries] = None
unavailableItems: Optional[List[UnavailableItem]] = None


class HomePossibleDeliveryServices(BaseModel):
deliveryServices: List[HomeDeliveryService]


class HomeDeliveryServicesResponse(BaseModel):
possibleDeliveryServices: Optional[HomePossibleDeliveryServices]
possibleDeliveryServices: Optional[HomePossibleDeliveryServices] = None


def parse_home_delivery_services(
constants: Constants, response: dict[str, Any]
) -> list[types.DeliveryService]:
parsed_response = HomeDeliveryServicesResponse(**response)
parsed_response = HomeDeliveryServicesResponse.model_validate(response)

res: list[types.DeliveryService] = []
if not parsed_response.possibleDeliveryServices:
Expand Down Expand Up @@ -184,8 +179,8 @@ def parse_home_delivery_services(

class PickUpPoint(BaseModel):
metadata: Metadata
timeWindows: Optional[TimeWindows]
identifier: Optional[str]
timeWindows: Optional[TimeWindows] = None
identifier: Optional[str] = None


class PossiblePickUpPoints(BaseModel):
Expand All @@ -203,18 +198,18 @@ class CollectPossibleDeliveries(BaseModel):

class CollectDeliveryService(BaseModel):
fulfillmentMethodType: str
solution: Optional[str]
solutionPrice: Optional[SolutionPrice]
possibleDeliveries: Optional[CollectPossibleDeliveries]
unavailableItems: Optional[List[UnavailableItem]]
solution: Optional[str] = None
solutionPrice: Optional[SolutionPrice] = None
possibleDeliveries: Optional[CollectPossibleDeliveries] = None
unavailableItems: Optional[List[UnavailableItem]] = None


class CollectPossibleDeliveryServices(BaseModel):
deliveryServices: List[CollectDeliveryService]


class CollectDeliveryServicesResponse(BaseModel):
possibleDeliveryServices: Optional[CollectPossibleDeliveryServices]
possibleDeliveryServices: Optional[CollectPossibleDeliveryServices] = None


def get_service_provider(constants: Constants, pickup_point: PickUpPoint) -> str | None:
Expand All @@ -230,7 +225,7 @@ def get_service_provider(constants: Constants, pickup_point: PickUpPoint) -> str
def parse_collect_delivery_services(
constants: Constants, response: dict[str, Any]
) -> list[types.DeliveryService]:
parsed_response = CollectDeliveryServicesResponse(**response)
parsed_response = CollectDeliveryServicesResponse.model_validate(response)

res: list[types.DeliveryService] = []
if not parsed_response.possibleDeliveryServices:
Expand All @@ -247,7 +242,7 @@ def parse_collect_delivery_services(
for delivery in service.possibleDeliveries.deliveries:
for point in delivery.possiblePickUpPoints.pickUpPoints:
date = (
point.timeWindows.earliestPossibleSlot.fromDateTime
point.timeWindows.earliestPossibleSlot.fromDateTime.date()
if point.timeWindows and point.timeWindows.earliestPossibleSlot
else None
)
Expand Down
6 changes: 3 additions & 3 deletions src/ikea_api/wrappers/parsers/pip_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class CatalogRef(BaseModel):


class CatalogRefs(BaseModel):
products: Optional[CatalogRef]
products: Optional[CatalogRef] = None


class ResponsePipItem(BaseModel):
Expand All @@ -37,12 +37,12 @@ def get_category_name_and_url(catalog_refs: CatalogRefs):
def parse_pip_item(response: dict[str, Any]) -> types.PipItem | None:
if not response:
return
parsed_item = ResponsePipItem(**response)
parsed_item = ResponsePipItem.model_validate(response)
category_name, category_url = get_category_name_and_url(parsed_item.catalogRefs)
return types.PipItem(
item_code=parsed_item.id,
price=parsed_item.priceNumeral,
url=parsed_item.pipUrl,
url=str(parsed_item.pipUrl),
category_name=category_name,
category_url=category_url,
)
8 changes: 4 additions & 4 deletions src/ikea_api/wrappers/parsers/purchases.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class HistoryDateAndTime(BaseModel):


class HistoryTotalCost(BaseModel):
value: Optional[int]
value: Optional[int] = None


class HistoryItem(BaseModel):
Expand All @@ -85,7 +85,7 @@ class ResponseHistory(BaseModel):


def parse_status_banner_order(response: dict[str, Any]) -> types.StatusBannerOrder:
order = ResponseStatusBanner(**response)
order = ResponseStatusBanner.model_validate(response)
return types.StatusBannerOrder(
purchase_date=order.data.order.dateAndTime.date,
delivery_date=order.data.order.deliveryMethods[
Expand All @@ -95,7 +95,7 @@ def parse_status_banner_order(response: dict[str, Any]) -> types.StatusBannerOrd


def parse_costs_order(response: dict[str, Any]) -> types.CostsOrder:
order = ResponseCosts(**response)
order = ResponseCosts.model_validate(response)
costs = order.data.order.costs
return types.CostsOrder(
delivery_cost=costs.delivery.value, total_cost=costs.total.value
Expand All @@ -109,7 +109,7 @@ def get_history_datetime(item: HistoryItem) -> str:
def parse_history(
constants: Constants, response: dict[str, Any]
) -> list[types.PurchaseHistoryItem]:
history = ResponseHistory(**response)
history = ResponseHistory.model_validate(response)
return [
types.PurchaseHistoryItem(
id=i.id,
Expand Down
18 changes: 9 additions & 9 deletions src/ikea_api/wrappers/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@


class ChildItem(BaseModel):
name: Optional[str]
name: Optional[str] = None
item_code: str
weight: float
qty: int
Expand All @@ -17,20 +17,20 @@ class ParsedItem(BaseModel):
is_combination: bool
item_code: str
name: str
image_url: Optional[str]
image_url: Optional[str] = None
weight: float
child_items: List[ChildItem]
price: int
url: str
category_name: Optional[str]
category_url: Optional[HttpUrl]
category_name: Optional[str] = None
category_url: Optional[HttpUrl] = None


class IngkaItem(BaseModel):
is_combination: bool
item_code: str
name: str
image_url: Optional[str]
image_url: Optional[str] = None
weight: float
child_items: List[ChildItem]

Expand All @@ -39,8 +39,8 @@ class PipItem(BaseModel):
item_code: str
price: int
url: str
category_name: Optional[str]
category_url: Optional[HttpUrl]
category_name: Optional[str] = None
category_url: Optional[HttpUrl] = None


class UnavailableItem(BaseModel):
Expand All @@ -50,10 +50,10 @@ class UnavailableItem(BaseModel):

class DeliveryService(BaseModel):
is_available: bool
date: Optional[datetime.date]
date: Optional[datetime.date] = None
type: str
price: int
service_provider: Optional[str]
service_provider: Optional[str] = None
unavailable_items: List[UnavailableItem]


Expand Down
8 changes: 4 additions & 4 deletions src/ikea_api/wrappers/wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ def get_purchase_info(
)
status_banner, costs = run_with_requests(endpoint)
return types.PurchaseInfo(
**parse_status_banner_order(status_banner).dict(),
**parse_costs_order(costs).dict(),
**parse_status_banner_order(status_banner).model_dump(),
**parse_costs_order(costs).model_dump(),
)


Expand All @@ -50,7 +50,7 @@ class _ExtensionsData(BaseModel):

class _Extensions(BaseModel):
code: str
data: Optional[_ExtensionsData]
data: Optional[_ExtensionsData] = None


class _CartErrorRef(BaseModel):
Expand All @@ -68,7 +68,7 @@ def add_items_to_cart(cart: Cart, items: dict[str, int]) -> types.CannotAddItems
break
except GraphQLError as exc:
for error_dict in exc.errors:
error = _CartErrorRef(**error_dict)
error = _CartErrorRef.model_validate(error_dict)
if error.extensions.code != "INVALID_ITEM_NUMBER":
continue
if not error.extensions.data:
Expand Down
Loading

0 comments on commit 1f0bf76

Please sign in to comment.