Skip to content
This repository was archived by the owner on Aug 14, 2026. It is now read-only.

Commit f038c8a

Browse files
committed
Cleared up code and fixed versions for new version
1 parent 32ae906 commit f038c8a

7 files changed

Lines changed: 957 additions & 852 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
1+
## v1.1.0 (2025-10-28)
2+
3+
### What's Changed
4+
5+
* Bumped minimal pydantic version from 2.8 to 2.11.
6+
* Added new constant: `UNSET`. Used for differentiating None from missing (or unset) values. `UnsetField`/`UnsetOrNoneField` were also added as annotated pydantic fields.
7+
* Now all requests can also pass request header as argument (see Clopos).
8+
* Added function `_build_request_lambda` to enable subclasses to override without overriding whole `__getattribute__`. Can be used to enforce some arguments.
9+
110
## v1.0.5 (2025-10-20)
211

312
### What's Changed
413

5-
Fixed and made integrify-core a namespace package.
14+
* Fixed and made integrify-core a namespace package.
615

716
## v1.0.3 (2025-07-19)
817

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "integrify-core"
7-
version = "1.0.5"
7+
version = "1.1.0"
88
description = "Integrify API inteqrasiyalarını rahatlaşdıran bünovrə (core) kitabxanadır."
99
authors = [{ name = "mmzeynalli", email = "miradil.zeynalli@gmail.com" }]
1010
requires-python = ">=3.9"
@@ -35,7 +35,7 @@ classifiers = [
3535
"Topic :: Internet :: WWW/HTTP :: HTTP Servers",
3636
"Topic :: Internet :: WWW/HTTP",
3737
]
38-
dependencies = ["pydantic>=2.8.2,<3", "httpx>=0.27.2,<0.28"]
38+
dependencies = ["pydantic>=2.11.10,<3", "httpx>=0.27.2,<0.28"]
3939

4040
[project.urls]
4141
Homepage = "https://integrify.mmzeynalli.dev/"

src/integrify/schemas.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import json
22
from typing import Any, ClassVar, Generic, Union
33

4-
from pydantic import BaseModel, Field, field_validator, model_serializer
4+
from pydantic import BaseModel, Field, field_validator
55
from typing_extensions import TypedDict
66

7-
from integrify.utils import UNSET, _ResponseT
7+
from integrify.utils import _ResponseT
88

99

1010
class APIResponse(BaseModel, Generic[_ResponseT]):
@@ -66,9 +66,3 @@ def from_args(cls, *args, **kwds):
6666
EYNİ OLMALIDIR, əks halda, bu method yararsızdır.
6767
"""
6868
return cls.model_validate({**dict(zip(cls.get_input_fields(), args)), **kwds})
69-
70-
@model_serializer(mode='wrap')
71-
def _serialize(self, serializer):
72-
data = serializer(self)
73-
# Exclude UNSET values
74-
return {k: v for k, v in data.items() if v is not UNSET}

src/integrify/utils.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def __new__(cls):
2020
return cls._instance
2121

2222
def __repr__(self):
23-
return 'UNSET'
23+
return '<UNSET>'
2424

2525
def __str__(self):
2626
return 'UNSET'
@@ -30,18 +30,17 @@ def __bool__(self):
3030

3131

3232
UNSET = UnsetType()
33-
"""Set olunmamış argument dəyəri"""
3433

3534
Unset = Union[T, Literal[UNSET]] # type: ignore[valid-type]
3635
""" Optional argument tipi """
3736

3837
UnsetOrNone = Union[T, Literal[UNSET], None] # type: ignore[valid-type]
3938
"""None dəyəri ala bilən optional argument tipi"""
4039

41-
UnsetField = Annotated[Unset[T], Field(default=UNSET)]
40+
UnsetField = Annotated[Unset[T], Field(default=UNSET, exclude_if=lambda x: x is UNSET)]
4241
"""Pydantic üçün set olunmamış argument dəyəri"""
4342

44-
UnsetOrNoneField = Annotated[UnsetOrNone[T], Field(default=UNSET)]
43+
UnsetOrNoneField = Annotated[UnsetOrNone[T], Field(default=UNSET, exclude_if=lambda x: x is UNSET)]
4544
"""Pydantic üçün set olunmamış və None dəyəri ala bilən argument dəyəri"""
4645

4746

tests/mocks.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ def test_ok_response():
1212
)
1313

1414

15+
@pytest.fixture(scope='package')
16+
def test_ok_response2():
17+
return Response(
18+
status_code=200,
19+
json={'data1': 'output1'},
20+
)
21+
22+
1523
@pytest.fixture(scope='package')
1624
def test_error_response():
1725
return Response(

tests/test_base.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from integrify.api import APIClient, APIPayloadHandler
1010
from integrify.schemas import PayloadBaseModel
11+
from integrify.utils import UNSET, UnsetField
1112

1213

1314
class RequestSchema(PayloadBaseModel):
@@ -18,11 +19,21 @@ class RequestWithURLParamSchema(RequestSchema):
1819
URL_PARAM_FIELDS = {'data1'}
1920

2021

22+
class RequestWithUnset(PayloadBaseModel):
23+
data1: str
24+
data2: UnsetField[str]
25+
26+
2127
class ResponseSchema(BaseModel):
2228
data1: str
2329
data2: str
2430

2531

32+
class ResponseSchemaWithUnset(BaseModel):
33+
data1: str
34+
data2: UnsetField[str]
35+
36+
2637
def test_unimplemented_function(api_client: APIClient):
2738
with pytest.raises(AttributeError):
2839
api_client.login()
@@ -189,3 +200,43 @@ def __init__(self):
189200
resp = dry_api_client.test(data1='input1')
190201
assert isinstance(resp['data'], dict)
191202
assert resp['data']['data1'] == 'input1'
203+
204+
205+
def test_unset_request(dry_api_client):
206+
class Handler(APIPayloadHandler):
207+
def __init__(self):
208+
super().__init__(RequestWithUnset, ResponseSchema)
209+
210+
dry_api_client.add_url('test', 'url', 'GET')
211+
dry_api_client.add_handler('test', Handler)
212+
resp = dry_api_client.test(data1='input1', data2='input2')
213+
assert isinstance(resp['data'], dict)
214+
assert resp['data']['data1'] == 'input1'
215+
assert resp['data']['data2'] == 'input2'
216+
217+
218+
def test_unset_request2(dry_api_client):
219+
class Handler(APIPayloadHandler):
220+
def __init__(self):
221+
super().__init__(RequestWithUnset, ResponseSchema)
222+
223+
dry_api_client.add_url('test', 'url', 'GET')
224+
dry_api_client.add_handler('test', Handler)
225+
resp = dry_api_client.test(data1='input1')
226+
assert isinstance(resp['data'], dict)
227+
assert resp['data']['data1'] == 'input1'
228+
assert 'data2' not in resp['data']
229+
230+
231+
def test_unset_response(api_client: APIClient, test_ok_response2, mocker: MockerFixture):
232+
class Handler(APIPayloadHandler):
233+
def __init__(self):
234+
super().__init__(RequestWithUnset, ResponseSchemaWithUnset)
235+
236+
with mocker.patch('httpx.Client.request', return_value=test_ok_response2):
237+
api_client.add_url('test', 'url', 'GET')
238+
api_client.add_handler('test', Handler)
239+
resp = api_client.test(data1='input1', data2='input2')
240+
assert isinstance(resp.body, ResponseSchemaWithUnset)
241+
assert resp.body.data1 == 'output1'
242+
assert resp.body.data2 is UNSET

0 commit comments

Comments
 (0)