diff --git a/.gitignore b/.gitignore index b6a0bde..c8ddb5d 100644 --- a/.gitignore +++ b/.gitignore @@ -166,4 +166,5 @@ cython_debug/ test.py *.lcov site -.aider* \ No newline at end of file +.aider* +.venvs/ \ No newline at end of file diff --git a/Makefile b/Makefile index a797b11..0961357 100644 --- a/Makefile +++ b/Makefile @@ -32,11 +32,12 @@ test-live: .uv ifeq ($(OS),Windows_NT) @FOR %%v IN ($(PYTHON_VERSIONS)) DO \ uv venv --python %%v .venvs\%%v & \ - uv run coverage run --data-file=coverage\.coverage.py%%v -m pytest --durations=10 + set "VIRTUAL_ENV=.venvs\%%v" & \ + uv run --active coverage run --data-file=coverage\.coverage.py%%v -m pytest --durations=10 else for v in ${PYTHON_VERSIONS}; do \ uv venv --python $$v .venvs/$$v & \ - uv run coverage run --data-file=coverage/.coverage.py$$v -m pytest --durations=10; \ + uv run --python .venvs/$$v/bin/python coverage run --data-file=coverage/.coverage.py$$v -m pytest --durations=10; \ done endif @@ -45,11 +46,12 @@ test-local: .uv ifeq ($(OS),Windows_NT) @FOR %%v IN ($(PYTHON_VERSIONS)) DO \ uv venv --python %%v .venvs\%%v & \ - uv run coverage run --data-file=coverage\.coverage.py%%v -m pytest -m "not live" --durations=10 + set "VIRTUAL_ENV=.venvs\%%v" & \ + uv run --active coverage run --data-file=coverage\.coverage.py%%v -m pytest -m "not live" --durations=10 else for v in ${PYTHON_VERSIONS}; do \ uv venv --python $$v .venvs/$$v & \ - uv run coverage run --data-file=coverage/.coverage.py$$v -m pytest -m "not live" --durations=10; \ + VIRTUAL_ENV=.venvs/$$v uv run --active coverage run --data-file=coverage/.coverage.py$$v -m pytest -m "not live" --durations=10; \ done endif diff --git a/docs/az/docs/resources/api-reference.md b/docs/az/docs/resources/api-reference.md index 7ec7579..a6cbb41 100644 --- a/docs/az/docs/resources/api-reference.md +++ b/docs/az/docs/resources/api-reference.md @@ -42,3 +42,11 @@ options: members: - from_args + +## Extras + +::: integrify.utils._UNSET + +::: integrify.utils.Unsettable + +::: integrify.utils.UnsettableAndNone diff --git a/src/integrify/api.py b/src/integrify/api.py index 6043c90..abea84b 100644 --- a/src/integrify/api.py +++ b/src/integrify/api.py @@ -7,7 +7,8 @@ import httpx from integrify.logger import LOGGER_FUNCTION -from integrify.schemas import APIResponse, PayloadBaseModel, _ResponseT +from integrify.schemas import APIResponse, PayloadBaseModel +from integrify.utils import _UNSET, _ResponseT class APIClient: @@ -91,15 +92,22 @@ def __getattribute__(self, name: str) -> Any: if name not in self.urls: raise - # "Axtarılan" funksiyanın adından istifadə edərək, lazımi endpoint, metod və handler-i - # taparaq, sorğunu icra edirik. - base_url = self.base_url or self.urls[name]['base_url'] - url = urljoin(base_url, self.urls[name]['url']) - verb = self.urls[name]['verb'] - handler = self.handlers.get(name, self.default_handler) + # "Axtarılan" funksiyanın adından istifadə edərək, lazımi endpoint, metod və handler-i + # taparaq, sorğunu icra edirik. + base_url = self.base_url or self.urls[name]['base_url'] + url = urljoin(base_url, self.urls[name]['url']) + verb = self.urls[name]['verb'] + handler = self.handlers.get(name, self.default_handler) - func = self.request_executor.request_function - return lambda *args, **kwds: func(url, verb, handler, *args, **kwds) + func = self.request_executor.request_function + return lambda *args, **kwds: func( + url, + verb, + handler, + # Exclude unset values, to trigger pydantic defaults + *(arg for arg in args if arg is not _UNSET), + **{k: v for k, v in kwds.items() if v is not _UNSET}, + ) class APIPayloadHandler: diff --git a/src/integrify/epoint/client.py b/src/integrify/epoint/client.py index 4764666..e874059 100644 --- a/src/integrify/epoint/client.py +++ b/src/integrify/epoint/client.py @@ -24,6 +24,7 @@ TransactionStatusResponseSchema, ) from integrify.schemas import APIResponse +from integrify.utils import _UNSET, Unsettable __all__ = ['EPointClientClass'] @@ -80,7 +81,7 @@ def pay( amount: Numeric, currency: str, order_id: str, - description: Optional[str] = None, + description: Unsettable[str] = _UNSET, **extra: Any, ) -> APIResponse[RedirectUrlResponseSchema]: """Ödəniş sorğusu @@ -190,7 +191,7 @@ def pay_and_save_card( amount: Numeric, currency: str, order_id: str, - description: Optional[str] = None, + description: Unsettable[str] = _UNSET, ) -> APIResponse[RedirectUrlWithCardIdResponseSchema]: """Ödəniş və kartı yadda saxlama sorğusu @@ -224,7 +225,7 @@ def payout( currency: str, order_id: str, card_id: str, - description: Optional[str] = None, + description: Unsettable[str] = _UNSET, ) -> APIResponse[BaseResponseSchema]: """Hesabınızda olan pulu karta nağdlaşdırmaq sorğusu @@ -254,7 +255,7 @@ def refund( self, transaction_id: str, currency: str, - amount: Optional[Numeric] = None, + amount: Unsettable[Numeric], ) -> APIResponse[MinimalResponseSchema]: """Keçmiş ödənişi tam və ya yarımçıq geri qaytarma sorğusu @@ -291,7 +292,7 @@ def split_pay( order_id: str, split_user_id: str, split_amount: Numeric, - description: Optional[str] = None, + description: Unsettable[str] = _UNSET, **extra: Any, ) -> APIResponse[RedirectUrlResponseSchema]: """Ödənişi başqa EPoint istifadəçisi ilə bölüb ödəmə sorğusu @@ -326,7 +327,7 @@ def split_pay_with_saved_card( card_id: str, split_user_id: str, split_amount: Numeric, - description: Optional[str] = None, + description: Unsettable[str] = _UNSET, ) -> APIResponse[SplitPayWithSavedCardResponseSchema]: """Saxlanılmış kartla ödənişi başqa EPoint istifadəçisi ilə bölüb ödəmə sorğusu @@ -358,7 +359,7 @@ def split_pay_and_save_card( order_id: str, split_user_id: str, split_amount: Numeric, - description: Optional[str] = None, + description: Unsettable[str] = _UNSET, ) -> APIResponse[RedirectUrlWithCardIdResponseSchema]: """Ödənişi başqa EPoint istifadəçisi ilə bölüb ödəmə və kartı saxlama sorğusu diff --git a/src/integrify/kapital/client.py b/src/integrify/kapital/client.py index e488082..ab5555b 100644 --- a/src/integrify/kapital/client.py +++ b/src/integrify/kapital/client.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from typing import SupportsFloat as Numeric from integrify.api import APIClient, APIResponse @@ -29,6 +29,7 @@ ProcessPaymentWithSavedCardResponseSchema, RefundOrderResponseSchema, ) +from integrify.utils import _UNSET, Unsettable __all__ = ['KapitalClientClass'] @@ -88,7 +89,7 @@ def pay_with_saved_card( token: int, amount: Numeric, currency: str, - description: Optional[str] = None, + description: Unsettable[str] = _UNSET, ) -> APIResponse[BaseResponseSchema[ProcessPaymentWithSavedCardResponseSchema]]: """ Yadda saxlanmış kartdan ödəniş etmək üçün sorğu @@ -141,7 +142,7 @@ def create_order( self, amount: Numeric, currency: str, - description: Optional[str] = None, + description: Unsettable[str] = _UNSET, ) -> APIResponse[BaseResponseSchema[CreateOrderResponseSchema]]: """Ödəniş sorğusu @@ -248,7 +249,7 @@ def save_card( self, amount: Numeric, currency: str, - description: Optional[str] = None, + description: Unsettable[str] = _UNSET, ) -> APIResponse[BaseResponseSchema[CreateOrderResponseSchema]]: """Kartı saxlamaq üçün ödəniş sorğusu @@ -281,7 +282,7 @@ def pay_and_save_card( self, amount: Numeric, currency: str, - description: Optional[str] = None, + description: Unsettable[str] = _UNSET, ) -> APIResponse[BaseResponseSchema[CreateOrderResponseSchema]]: """Kartı saxlamaq və ödəniş etmək üçün ödəniş sorğusu @@ -392,7 +393,7 @@ def order_with_saved_card( self, amount: Numeric, currency: str, - description: Optional[str] = None, + description: Unsettable[str] = _UNSET, ) -> APIResponse[BaseResponseSchema[CreateOrderResponseSchema]]: """ Bu funksiya sadece KapitalClientClass daxilinde istifade olunur! diff --git a/src/integrify/kapital/env.py b/src/integrify/kapital/env.py index 4eb245a..08bda25 100644 --- a/src/integrify/kapital/env.py +++ b/src/integrify/kapital/env.py @@ -3,7 +3,7 @@ from typing import Optional from warnings import warn -from integrify.schemas import Environment +from integrify.utils import Environment VERSION = '2024.10.19' diff --git a/src/integrify/schemas.py b/src/integrify/schemas.py index 7a90280..d90600e 100644 --- a/src/integrify/schemas.py +++ b/src/integrify/schemas.py @@ -1,15 +1,9 @@ import json -from enum import Enum -from typing import ClassVar, Generic, TypeVar, Union +from typing import ClassVar, Generic, Union from pydantic import BaseModel, Field, field_validator -_ResponseT = TypeVar('_ResponseT', bound=Union[BaseModel, dict]) - - -class Environment(str, Enum): - TEST = 'test' - PROD = 'prod' +from integrify.utils import _ResponseT class APIResponse(BaseModel, Generic[_ResponseT]): diff --git a/src/integrify/utils.py b/src/integrify/utils.py new file mode 100644 index 0000000..4d341af --- /dev/null +++ b/src/integrify/utils.py @@ -0,0 +1,23 @@ +from enum import Enum +from typing import Literal, TypeVar, Union + +from pydantic import BaseModel + +_ResponseT = TypeVar('_ResponseT', bound=Union[BaseModel, dict]) +"""Dynamic response type.""" + +T = TypeVar('T') + +_UNSET = object() +"""Set olunmamış argument dəyəri""" + +Unsettable = Union[T, Literal[_UNSET]] # type: ignore[valid-type] +""" Optional argument tipi """ + +UnsettableAndNone = Union[T, Literal[_UNSET], None] # type: ignore[valid-type] +"""None dəyəri ala bilən optional argument tipi""" + + +class Environment(str, Enum): + TEST = 'test' + PROD = 'prod'