From 90fe4ac2adee306ee30a568884336e6a7ca18ee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Radek=20Gol=C3=A1=C5=88=20jr=2E?= Date: Thu, 31 Jul 2025 20:48:30 +0200 Subject: [PATCH] initial commit --- .python-version | 1 + .vscode/settings.json | 12 ++ README.md | 0 pyproject.toml | 17 +++ src/comgate/__init__.py | 12 ++ src/comgate/client.py | 64 +++++++++ src/comgate/models.py | 281 ++++++++++++++++++++++++++++++++++++++++ src/comgate/py.typed | 0 uv.lock | 181 ++++++++++++++++++++++++++ 9 files changed, 568 insertions(+) create mode 100644 .python-version create mode 100644 .vscode/settings.json create mode 100644 README.md create mode 100644 pyproject.toml create mode 100644 src/comgate/__init__.py create mode 100644 src/comgate/client.py create mode 100644 src/comgate/models.py create mode 100644 src/comgate/py.typed create mode 100644 uv.lock diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..24ee5b1 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..59c282f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "cSpell.words": [ + "APPLEPAY", + "CARDNO", + "Comgate", + "ESHOP", + "GOOGLEPAY", + "RDPART", + "THIRDPARTY", + "THREEDS" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..acc7346 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,17 @@ +[project] +name = "comgate" +version = "0.1.0" +description = "A Python Client for Comgate API" +readme = "README.md" +authors = [ + { name = "Radek Goláň jr.", email = "shield@shielddagger.com" } +] +requires-python = ">=3.13" +dependencies = [ + "pydantic[email]>=2.11.7", + "requests>=2.32.4", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/src/comgate/__init__.py b/src/comgate/__init__.py new file mode 100644 index 0000000..d005b37 --- /dev/null +++ b/src/comgate/__init__.py @@ -0,0 +1,12 @@ +from .client import Comgate +from .models import Category, Country, Currency, Delivery, PaymentRequest, RefundRequest + +__all__ = [ + "Comgate", + "Category", + "Country", + "Currency", + "Delivery", + "PaymentRequest", + "RefundRequest", +] diff --git a/src/comgate/client.py b/src/comgate/client.py new file mode 100644 index 0000000..cd1d888 --- /dev/null +++ b/src/comgate/client.py @@ -0,0 +1,64 @@ +import json +import logging +from typing import final + +from requests import Session +from requests.auth import HTTPBasicAuth + +from .models import Country, Currency, Payment, PaymentMethod, PaymentRequest + + +@final +class Comgate: + """Comgate Payments API Client""" + + def __init__(self, merchant: int, password: str, test: bool = False): + """Create a Comgate API Client + + Args: + merchant (int): Merchant Connection ID + password (str): Merchant Connection Password + test (bool): Configure the client for testing purposes + """ + self.session = Session() + self.session.auth = HTTPBasicAuth(str(merchant), password) + self.test = test + + self.base_url = "https://payments.comgate.cz" + self.create_endpoint = "/v2.0/payment.json" + self.payment_endpoint = "/v2.0/payment/transId/%s.json" + self.refund_endpoint = "/v2.0/refund.json" + + def create( + self, + country: Country, + price: int, + currency: Currency, + label: str, + refId: str, + fullName: str, + email: str | None = None, + phone: str | None = None, + method: PaymentMethod = PaymentMethod.ALL, + **kwargs, # pyright: ignore[reportMissingParameterType, reportUnknownParameterType] + ) -> Payment: + request = PaymentRequest( + test=self.test, + country=country, + price=price * 100, + curr=currency, + label=label, + refId=refId, + fullName=fullName, + email=email, + phone=phone, + method=method, + **kwargs, # pyright: ignore[reportUnknownArgumentType] + ) + response = self.session.post( + self.base_url + self.create_endpoint, + json=json.loads(request.model_dump_json()), # pyright: ignore[reportAny] + ) + if not response.ok: + logging.root.error("Failed Comgate Request: %s", request.model_dump_json()) + return Payment.model_validate_json(response.content) diff --git a/src/comgate/models.py b/src/comgate/models.py new file mode 100644 index 0000000..97a1cda --- /dev/null +++ b/src/comgate/models.py @@ -0,0 +1,281 @@ +from enum import Enum, IntEnum +import sys +from pydantic import BaseModel, EmailStr, HttpUrl, Field, model_validator + + +class Country(Enum): + ALL = "ALL" + AT = "AT" + BE = "BE" + CY = "CY" + CZ = "CZ" + DE = "DE" + EE = "EE" + EL = "EL" + ES = "ES" + FI = "FI" + FR = "FR" + GB = "GB" + HR = "HR" + HU = "HU" + IE = "IE" + IT = "IT" + LT = "LT" + LU = "LU" + LV = "LV" + MT = "MT" + NL = "NL" + NO = "NO" + PL = "PL" + PT = "PT" + RO = "RO" + SI = "SI" + SK = "SK" + SE = "SE" + US = "US" + + +class Currency(Enum): + CZK = "CZK" + EUR = "EUR" + PLN = "PLN" + HUF = "HUF" + USD = "USD" + GBP = "GBP" + RON = "RON" + HRK = "HRK" + NOK = "NOK" + SEK = "SEK" + + +PRICE_RANGE = { + Currency.CZK: (1_00, 1_000_000_00), + Currency.EUR: (10, 40_000_00), + Currency.PLN: (1_00, 170_000_00), + Currency.HUF: (100_00, 12_500_000_00), + Currency.USD: (1_00, 45_000_00), + Currency.GBP: (1_00, 35_000_00), + Currency.RON: (5_00, 190_000_00), + Currency.HRK: ( + 1_00, + sys.maxsize, + ), # HACK: This seems like a doc gaffe. We don't have a value so we just use maxsize, but it's probably much lower than that + Currency.NOK: (50, 400_000_00), + Currency.SEK: (50, 390_000_00), +} + + +class PaymentMethod(Enum): + ALL = "ALL" + BANK_ALL = "BANK_ALL" + BANK_ONLY = "BANK_ONLY" + CARD_ALL = "CARD_ALL" + LATER_ALL = "LATER_ALL" + LOAN_ALL = "LOAN_ALL" + PART_ALL = "PART_ALL" + APPLEPAY_REDIRECT = "APPLEPAY_REDIRECT" + GOOGLEPAY_REDIRECT = "GOOGLEPAY_REDIRECT" + + +class Delivery(Enum): + HOME_DELIVERY = "HOME_DELIVERY" + PICKUP = "PICKUP" + ELECTRONIC_DELIVERY = "ELECTRONIC_DELIVERY" + + +class Category(Enum): + PHYSICAL_GOODS_ONLY = "PHYSICAL_GOODS_ONLY" + OTHER = "OTHER" + + +class PaymentRequest(BaseModel): + test: bool = False + country: Country = Country.CZ + price: int + curr: Currency + label: str = Field(max_length=16, min_length=1) + refId: str + method: PaymentMethod + account: str | None = None + + email: EmailStr | None = None + phone: str | None = None + + fullName: str + + billingAddrCity: str | None = None + billingAddrStreet: str | None = None + billingAddrPostalCode: str | None = None + billingAddrCountry: str | None = None + + homeDeliveryCity: str | None = None + homeDeliveryStreet: str | None = None + homeDeliveryPostalCode: str | None = None + homeDeliveryCountry: str | None = None + + category: Category | None = None + name: str | None = None + lang: str | None = "en" + preauth: bool = False + initRecurring: bool = False + verification: bool = False + + expirationTime: str | None = Field(default=None, pattern=r"^[1-9][0-9]*(m|h|d)$") + dynamicExpiration: bool = True + + url_paid: str | None = None + url_cancelled: str | None = None + url_pending: str | None = None + chargeUnregulatedCardFees: bool | None = None + enableApplePayGooglePay: bool | None = None + + @model_validator(mode="after") + def _check_price(self): + v_min, v_max = PRICE_RANGE[self.curr] + if self.price not in range(v_min, v_max): + raise ValueError( + f"Unsupported price value: {self.price}, it must be between {v_min} amd {v_max}" + ) + return self + + @model_validator(mode="after") + def _check_contact(self): + if self.phone is None and self.email is None: + raise ValueError("At least phone or email must be provided") + return self + + +class PaymentCode(IntEnum): + OK = 0 + UNKNOWN_ERROR = 1100 + UNSUPPORTED_LANGUAGE = 1102 + INCORRECT_METHOD = 1103 + PAYMENT_LOAD_ERROR = 1104 + UNSUPPORTED_PRICE = 1107 + DATABASE_ERROR = 1200 + UNKNOWN_STORE = 1301 + MISSING_LINK_LANG = 1303 + INVALID_CATEGORY = 1304 + MISSING_PRODUCT_DESC = 1305 + CHANGE_METHOD = 1306 + METHOD_NOT_ALLOWED = 1308 + WRONG_AMOUNT = 1309 + UNKNOWN_CURRENCY = 1310 + INVALID_BANK_ID = 1311 + RECURRING_UNAVAILABLE = 1316 + RECURRING_UNSUPPORTED = 1317 + PAYMENT_SETUP_ERROR = 1319 + UNEXPECTED_DB_RESULT = 1399 + BAD_REQUEST = 1400 + UNEXPECTED_ERROR = 1500 + + +class Payment(BaseModel): + code: PaymentCode + message: str + transId: str | None = None + redirect: HttpUrl | None = None + + +class CancellationCode(IntEnum): + OK = 0 + CANNOT_CANCEL = 1400 + + +class Cancellation(BaseModel): + code: CancellationCode + message: str + + +class StatusCode(IntEnum): + OK = 0 + UNKNOWN_ERROR = 1100 + DATABASE_ERROR = 1200 + WRONG_QUERY = 1400 + UNEXPECTED_ERROR = 1500 + + +class PaymentStatus(Enum): + PENDING = "PENDING" + PAID = "PAID" + CANCELLED = "CANCELLED" + AUTHORIZED = "AUTHORIZED" + + +class FeeType(Enum): + EU_UNREGULATED = "EU_UNREGULATED" + NON_EU_BUSINESS = "NON_EU_BUSINESS" + NON_EU_CONSUMER = "NON_EU_CONSUMER" + EU_CONSUMER = "EU_CONSUMER" + + +class PaymentErrReason(Enum): + CUSTOMER_CLICK = "CUSTOMER_CLICK" + FRAUD_SUSPECTED = "FRAUD_SUSPECTED" + ESHOP_CANCELLED = "ESHOP_CANCELLED" + PROVIDER_REPORT = "PROVIDER_REPORT" + PROVIDER_TIMEOUT = "PROVIDER_TIMEOUT" + CUSTOMER_TIMEOUT = "CUSTOMER_TIMEOUT" + ACS_TIMEOUT = "ACS_TIMEOUT" + INVALID_CARDNO_EXPIRY = "INVALID_CARDNO_EXPIRY" + INVALID_CVC = "INVALID_CVC" + LIMIT_EXCEEDED = "LIMIT_EXCEEDED" + NO_FUNDS = "NO_FUNDS" + REJECTED_BY_BANK = "REJECTED_BY_BANK" + THREEDS_AUTH_FAIL = "3DS_AUTH_FAIL" + BANK_TIMEOUT = "BANK_TIMEOUT" + BANK_FORBIDDEN = "BANK_FORBIDDEN" + TRANSACTION_CANCELLED = "TRANSACTION_CANCELLED" + THIRDPARTY_APP_LIMIT_EXCEEDED = "3RDPART_APP_LIMIT_EXCEEDED" + UNAVAILABLE = "UNAVAILABLE" + BAD_ACCOUNT_TYPE = "BAD_ACCOUNT_TYPE" + NOT_SPECIFIED = "NOT_SPECIFIED" + + +class Status(BaseModel): + code: StatusCode + message: str + test: bool + price: int + curr: Currency + label: str + refId: str + payerId: str | None = None + method: PaymentMethod | None = None + account: str | None = None + email: EmailStr + name: str | None = None + phone: str | None = None + transId: str + status: PaymentStatus + payerName: str | None = None + payerAcc: str | None = None + fee: str | None = None + vs: str | None = None + cardValid: str | None = None + cardNumber: str | None = None + appliedFee: int | None = None + appliedFeeType: FeeType | None = None + paymentErrorReason: PaymentErrReason | None = None + + +class RefundRequest(BaseModel): + transId: str + amount: int + test: bool = False + refId: str | None = None + + +class RefundCode(IntEnum): + OK = 0 + UNKNOWN_ERROR = 1100 + DATABASE_ERROR = 1200 + WRONG_QUERY = 1400 + PAYMENT_CANCELLED = 1401 + AMOUNT_TOO_HIGH = 1402 + UNEXPECTED_ERROR = 1500 + + +class Refund(BaseModel): + code: RefundCode + message: str diff --git a/src/comgate/py.typed b/src/comgate/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..e2a99d2 --- /dev/null +++ b/uv.lock @@ -0,0 +1,181 @@ +version = 1 +requires-python = ">=3.13" + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, +] + +[[package]] +name = "certifi" +version = "2025.7.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/76/52c535bcebe74590f296d6c77c86dabf761c41980e1347a2422e4aa2ae41/certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995", size = 163981 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/52/34c6cf5bb9285074dc3531c437b3919e825d976fde097a7a73f79e726d03/certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2", size = 162722 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622 }, + { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435 }, + { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653 }, + { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231 }, + { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243 }, + { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442 }, + { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147 }, + { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057 }, + { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454 }, + { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174 }, + { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166 }, + { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064 }, + { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641 }, + { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626 }, +] + +[[package]] +name = "comgate" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "pydantic", extra = ["email"] }, + { name = "requests" }, +] + +[package.metadata] +requires-dist = [ + { name = "pydantic", extras = ["email"], specifier = ">=2.11.7" }, + { name = "requests", specifier = ">=2.32.4" }, +] + +[[package]] +name = "dnspython" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632 }, +] + +[[package]] +name = "email-validator" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dnspython" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/ce/13508a1ec3f8bb981ae4ca79ea40384becc868bfae97fd1c942bb3a001b1/email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7", size = 48967 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/ee/bf0adb559ad3c786f12bcbc9296b3f5675f529199bef03e2df281fa1fadb/email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631", size = 33521 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "pydantic" +version = "2.11.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782 }, +] + +[package.optional-dependencies] +email = [ + { name = "email-validator" }, +] + +[[package]] +name = "pydantic-core" +version = "2.33.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688 }, + { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808 }, + { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580 }, + { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859 }, + { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810 }, + { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498 }, + { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611 }, + { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924 }, + { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196 }, + { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389 }, + { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223 }, + { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473 }, + { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269 }, + { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921 }, + { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162 }, + { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560 }, + { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777 }, +] + +[[package]] +name = "requests" +version = "2.32.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847 }, +] + +[[package]] +name = "typing-extensions" +version = "4.14.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906 }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552 }, +] + +[[package]] +name = "urllib3" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795 }, +]