This commit is contained in:
2025-03-20 21:26:04 -03:00
parent 85cbc9269c
commit 1f19380f5c
20 changed files with 293 additions and 54 deletions

View File

@@ -9,31 +9,49 @@ from botocore.exceptions import ClientError
logger = Logger(__name__)
def _serialize(v):
if isinstance(v, datetime):
return v.isoformat()
if isinstance(v, IPv4Address):
return str(v)
if isinstance(v, (list, tuple)):
return [_serialize(x) for x in v]
if isinstance(v, dict):
return {k: _serialize(dv) for k, dv in v.items()}
return v
def _serialize_python_types(obj: Any) -> str | dict | list:
match obj:
case datetime():
return obj.isoformat()
case IPv4Address():
return str(obj)
case list() | tuple():
return [_serialize_python_types(v) for v in obj]
case dict():
return {k: _serialize_python_types(v) for k, v in obj.items()}
case _:
return obj
def serialize(obj: dict) -> dict:
return {k: TypeSerializer().serialize(_serialize(v)) for k, v in obj.items()}
serializer = TypeSerializer()
return {k: serializer.serialize(_serialize_python_types(v)) for k, v in obj.items()}
def deserialize(obj: dict) -> dict:
return {k: TypeDeserializer().deserialize(v) for k, v in obj.items()}
deserializer = TypeDeserializer()
return {k: deserializer.deserialize(v) for k, v in obj.items()}
def Key(pk: str, sk: str) -> dict[str, str]:
def Key(
val: str | tuple[str, ...],
*,
prefix: str | None = None,
delimiter: str = '#',
) -> str:
if not prefix and not isinstance(val, tuple):
return val
if isinstance(val, str):
val = (val,)
if prefix:
val = (prefix,) + val
return delimiter.join(val)
def KeyPair(pk: str, sk: str) -> dict[str, str]:
return {
'id': pk,
'sk': sk,

View File

@@ -4,7 +4,8 @@ from typing import TYPE_CHECKING, Annotated, Any
import ftfy
from pycpfcnpj import cpfcnpj
from pydantic import BaseModel, Field, GetCoreSchemaHandler
from pydantic import BaseModel, Field, GetCoreSchemaHandler, GetJsonSchemaHandler
from pydantic.json_schema import JsonSchemaValue
from pydantic_core import CoreSchema, core_schema
from pydantic_extra_types.payment import PaymentCardNumber
@@ -47,6 +48,14 @@ else:
return name
@classmethod
def __get_pydantic_json_schema__(
cls, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
) -> JsonSchemaValue:
field_schema = handler(core_schema)
field_schema.update(type='string', format='name')
return field_schema
class PaymentCardValidation:
"""
@@ -145,11 +154,9 @@ if TYPE_CHECKING:
CnpjStr = Annotated[str, ...]
else:
class CpfStr(CpfCnpj):
...
class CpfStr(CpfCnpj): ...
class CnpjStr(CpfCnpj):
...
class CnpjStr(CpfCnpj): ...
if __name__ == '__main__':

View File

View File

@@ -16,7 +16,7 @@ dependencies = [
"glom>=24.11.0",
"orjson>=3.10.15",
"pycpfcnpj>=1.8",
"pydantic>=2.10.6",
"pydantic[email]>=2.10.6",
"pydantic-extra-types>=2.10.3",
"pytz>=2025.1",
"shortuuid>=1.0.13",

View File

@@ -1,7 +1,40 @@
from datetime import datetime
from ipaddress import IPv4Address
import pytest
from botocore.exceptions import ClientError
from layercake.dynamodb import DynamoDBPersistenceLayer, TransactItems
from layercake.dynamodb import (
DynamoDBPersistenceLayer,
Key,
KeyPair,
TransactItems,
serialize,
)
def test_serialize():
assert serialize(
{
'id': '123',
'sk': 'abc',
'date': datetime.fromisoformat('2025-03-20T18:29:10.713994'),
'ip': IPv4Address('127.0.0.1'),
}
) == {
'id': {'S': '123'},
'sk': {'S': 'abc'},
'date': {'S': '2025-03-20T18:29:10.713994'},
'ip': {'S': '127.0.0.1'},
}
def test_key():
assert Key(('122', 'abc'), prefix='schedules') == 'schedules#122#abc'
def test_keypair():
assert KeyPair('123', 'abc') == {'id': '123', 'sk': 'abc'}
def test_transact_write_items(dynamodb_client):
@@ -33,5 +66,6 @@ def test_transact_write_items(dynamodb_client):
},
cond_expr='attribute_not_exists(sk)',
)
with pytest.raises(ClientError):
user_layer.transact_write_items(transact)

View File

@@ -0,0 +1,21 @@
from layercake.funcs import omit, pick
def test_omit():
values = {'indigo': '#4b0082', 'navy': '#000080'}
assert omit(['indigo'], values) == {'navy': '#000080'}
assert omit(['test'], values) == values
def test_pick():
values = {'indigo': '#4b0082', 'navy': '#000080'}
assert pick(['navy'], values) == {'navy': '#000080'}
assert pick(['test'], values) == {}
def test_pick_default_val():
values = {'name': 'test'}
assert pick(['name', 'surname'], values, exclude_none=False, default=False) == {
'name': 'test',
'surname': False,
}

40
layercake/uv.lock generated
View File

@@ -232,6 +232,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/33/cf/1f7649b8b9a3543e042d3f348e398a061923ac05b507f3f4d95f11938aa9/cryptography-44.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:5f6f90b72d8ccadb9c6e311c775c8305381db88374c65fa1a68250aa8a9cb3a6", size = 3210957 },
]
[[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 = "elastic-transport"
version = "8.17.1"
@@ -272,6 +281,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ad/b4/5e707bca39062ba0b5227696a767db09767e5f09e869c6cb14aeb36e4b9d/elasticsearch_dsl-8.17.1-py3-none-any.whl", hash = "sha256:49ee12a6a8d43fcfc0af42b49649531a6ef228c9e4795325de27f6b309b62b6d", size = 158294 },
]
[[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 = "face"
version = "24.0.0"
@@ -319,6 +341,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/9c/a2/75fd80784ec33da8d39cf885e8811a4fbc045a90db5e336b8e345e66dbb2/glom-24.11.0-py3-none-any.whl", hash = "sha256:991db7fcb4bfa9687010aa519b7b541bbe21111e70e58fdd2d7e34bbaa2c1fbd", size = 102690 },
]
[[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 = "iniconfig"
version = "2.1.0"
@@ -362,7 +393,7 @@ dependencies = [
{ name = "glom" },
{ name = "orjson" },
{ name = "pycpfcnpj" },
{ name = "pydantic" },
{ name = "pydantic", extra = ["email"] },
{ name = "pydantic-extra-types" },
{ name = "pytz" },
{ name = "shortuuid" },
@@ -385,7 +416,7 @@ requires-dist = [
{ name = "glom", specifier = ">=24.11.0" },
{ name = "orjson", specifier = ">=3.10.15" },
{ name = "pycpfcnpj", specifier = ">=1.8" },
{ name = "pydantic", specifier = ">=2.10.6" },
{ name = "pydantic", extras = ["email"], specifier = ">=2.10.6" },
{ name = "pydantic-extra-types", specifier = ">=2.10.3" },
{ name = "pytz", specifier = ">=2025.1" },
{ name = "shortuuid", specifier = ">=1.0.13" },
@@ -491,6 +522,11 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/f4/3c/8cc1cc84deffa6e25d2d0c688ebb80635dfdbf1dbea3e30c541c8cf4d860/pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584", size = 431696 },
]
[package.optional-dependencies]
email = [
{ name = "email-validator" },
]
[[package]]
name = "pydantic-core"
version = "2.27.2"