cuenca-validations 2.1.27__tar.gz → 2.1.29__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/PKG-INFO +1 -1
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations/types/general.py +8 -1
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations/types/queries.py +5 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations/types/requests.py +21 -7
- cuenca_validations-2.1.29/cuenca_validations/validators.py +86 -0
- cuenca_validations-2.1.29/cuenca_validations/version.py +1 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations.egg-info/PKG-INFO +1 -1
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations.egg-info/SOURCES.txt +2 -1
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/tests/test_requests.py +29 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/tests/test_types.py +46 -0
- cuenca_validations-2.1.29/tests/test_validators.py +52 -0
- cuenca_validations-2.1.27/cuenca_validations/validators.py +0 -42
- cuenca_validations-2.1.27/cuenca_validations/version.py +0 -1
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/LICENSE +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/README.md +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations/__init__.py +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations/card_bins.py +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations/errors.py +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations/py.typed +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations/types/__init__.py +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations/types/card.py +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations/types/enums.py +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations/types/files.py +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations/types/helpers.py +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations/types/identities.py +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations/types/morals.py +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations/typing.py +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations.egg-info/dependency_links.txt +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations.egg-info/requires.txt +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations.egg-info/top_level.txt +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/setup.cfg +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/setup.py +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/tests/__init__.py +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/tests/test_card.py +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/tests/test_errors.py +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/tests/test_helpers.py +0 -0
- {cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/tests/test_statement.py +0 -0
|
@@ -3,6 +3,7 @@ from dataclasses import dataclass
|
|
|
3
3
|
from typing import Annotated, Any, Optional
|
|
4
4
|
|
|
5
5
|
from pydantic import (
|
|
6
|
+
AfterValidator,
|
|
6
7
|
AnyUrl,
|
|
7
8
|
Field,
|
|
8
9
|
HttpUrl,
|
|
@@ -11,7 +12,7 @@ from pydantic import (
|
|
|
11
12
|
StringConstraints,
|
|
12
13
|
)
|
|
13
14
|
|
|
14
|
-
from ..validators import sanitize_dict, sanitize_item
|
|
15
|
+
from ..validators import normalize_name, sanitize_dict, sanitize_item
|
|
15
16
|
from .enums import (
|
|
16
17
|
AccountUseType,
|
|
17
18
|
IncomeType,
|
|
@@ -33,6 +34,12 @@ NonEmptyStr = Annotated[
|
|
|
33
34
|
str, StringConstraints(strip_whitespace=True, min_length=1)
|
|
34
35
|
]
|
|
35
36
|
|
|
37
|
+
NormalizedName = Annotated[
|
|
38
|
+
str,
|
|
39
|
+
StringConstraints(strip_whitespace=True, min_length=2),
|
|
40
|
+
AfterValidator(normalize_name),
|
|
41
|
+
]
|
|
42
|
+
|
|
36
43
|
|
|
37
44
|
class SantizedDict(dict):
|
|
38
45
|
def __init__(self, *args, **kwargs):
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import datetime as dt
|
|
2
2
|
from typing import Annotated, Optional
|
|
3
3
|
|
|
4
|
+
from clabe import Clabe
|
|
4
5
|
from pydantic import (
|
|
5
6
|
BaseModel,
|
|
6
7
|
ConfigDict,
|
|
@@ -26,6 +27,7 @@ from .enums import (
|
|
|
26
27
|
TransferNetwork,
|
|
27
28
|
UserStatus,
|
|
28
29
|
)
|
|
30
|
+
from .general import NormalizedName
|
|
29
31
|
from .identities import Curp
|
|
30
32
|
|
|
31
33
|
MAX_PAGE_SIZE = 100
|
|
@@ -157,6 +159,9 @@ class UserQuery(QueryParams):
|
|
|
157
159
|
status: Optional[UserStatus] = None
|
|
158
160
|
identity_uri: Optional[str] = None
|
|
159
161
|
has_curp_document: Optional[bool] = None
|
|
162
|
+
clabe: Optional[Clabe] = None
|
|
163
|
+
curp: Optional[Curp] = None
|
|
164
|
+
name: Optional[NormalizedName] = None
|
|
160
165
|
|
|
161
166
|
|
|
162
167
|
class IdentityQuery(QueryParams):
|
{cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations/types/requests.py
RENAMED
|
@@ -56,6 +56,7 @@ from ..types.enums import (
|
|
|
56
56
|
WebhookObject,
|
|
57
57
|
)
|
|
58
58
|
from ..typing import DictStrAny
|
|
59
|
+
from ..validators import normalize_email, normalize_phone_number
|
|
59
60
|
from .card import (
|
|
60
61
|
Cvv,
|
|
61
62
|
ExpMonth,
|
|
@@ -543,6 +544,16 @@ class UserUpdateRequest(BaseRequest):
|
|
|
543
544
|
raise ValueError('At least one parameter must be provided')
|
|
544
545
|
return values
|
|
545
546
|
|
|
547
|
+
@field_validator('email_address', mode='before')
|
|
548
|
+
@classmethod
|
|
549
|
+
def validate_email_address(cls, v: Optional[str]) -> Optional[str]:
|
|
550
|
+
return normalize_email(v) if v else v
|
|
551
|
+
|
|
552
|
+
@field_validator('phone_number', mode='before')
|
|
553
|
+
@classmethod
|
|
554
|
+
def validate_phone_number(cls, v: Optional[str]) -> Optional[str]:
|
|
555
|
+
return normalize_phone_number(v) if v else v
|
|
556
|
+
|
|
546
557
|
@field_validator('beneficiaries')
|
|
547
558
|
@classmethod
|
|
548
559
|
def beneficiary_percentage(
|
|
@@ -641,13 +652,16 @@ class VerificationRequest(BaseRequest):
|
|
|
641
652
|
},
|
|
642
653
|
)
|
|
643
654
|
|
|
644
|
-
@
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
655
|
+
@model_validator(mode='before')
|
|
656
|
+
@classmethod
|
|
657
|
+
def normalize_recipient(cls, data):
|
|
658
|
+
_type = data.get('type')
|
|
659
|
+
recipient = data.get('recipient')
|
|
660
|
+
if _type == VerificationType.email:
|
|
661
|
+
data['recipient'] = normalize_email(recipient)
|
|
662
|
+
else:
|
|
663
|
+
data['recipient'] = normalize_phone_number(recipient)
|
|
664
|
+
return data
|
|
651
665
|
|
|
652
666
|
|
|
653
667
|
class VerificationAttemptRequest(BaseRequest):
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import datetime as dt
|
|
3
|
+
import re
|
|
4
|
+
import unicodedata
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from typing import Any, Callable, Optional, Union
|
|
7
|
+
|
|
8
|
+
SANITIZE_PHONE_NUMBER = re.compile(r'[+.()\-\s]')
|
|
9
|
+
STRIP_MX_MOBILE_PREFIX = re.compile(r'(52)(?:(?:044)|1)?(\d{10})$')
|
|
10
|
+
STRIP_US_DUPLICATE_PREFIX = re.compile(r'^11(\d{10})$')
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def normalize_email(email: str) -> str:
|
|
14
|
+
"""Lowercase email and strip plus labels from the local part.
|
|
15
|
+
|
|
16
|
+
mateohhr@Yahoo.com -> mateohhr@yahoo.com
|
|
17
|
+
guerradzul+cuenca@gmail.com -> guerradzul@gmail.com
|
|
18
|
+
"""
|
|
19
|
+
local, _, domain = email.partition('@')
|
|
20
|
+
return f'{local.split("+")[0]}@{domain}'.lower()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def normalize_phone_number(phone_number: str) -> str:
|
|
24
|
+
"""Sanitize and normalize phone numbers to E.164 format.
|
|
25
|
+
|
|
26
|
+
Handles:
|
|
27
|
+
- Special characters: +52 (55) 1234-5678 -> +525512345678
|
|
28
|
+
- MX mobile prefix: +5215512345678 -> +525512345678
|
|
29
|
+
- MX 044 prefix: +520445512345678 -> +525512345678
|
|
30
|
+
- US duplicate prefix: +116504401222 -> +16504401222
|
|
31
|
+
"""
|
|
32
|
+
pn = SANITIZE_PHONE_NUMBER.sub('', phone_number)
|
|
33
|
+
pn = STRIP_MX_MOBILE_PREFIX.sub(r'\1\2', pn)
|
|
34
|
+
pn = STRIP_US_DUPLICATE_PREFIX.sub(r'1\1', pn)
|
|
35
|
+
return f'+{pn}'
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def normalize_name(name: str) -> str:
|
|
39
|
+
"""Normalize names for search/index matching.
|
|
40
|
+
|
|
41
|
+
Strips accents, lowercases, and collapses internal whitespace.
|
|
42
|
+
|
|
43
|
+
Raúl Andrés -> raul andres
|
|
44
|
+
MARÍA JOSÉ -> maria jose
|
|
45
|
+
"""
|
|
46
|
+
collapsed = ' '.join(name.split())
|
|
47
|
+
nfkd = unicodedata.normalize('NFKD', collapsed)
|
|
48
|
+
return ''.join(c for c in nfkd if not unicodedata.combining(c)).lower()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def sanitize_dict(d: dict) -> dict:
|
|
52
|
+
for k, v in d.items():
|
|
53
|
+
d[k] = sanitize_item(v)
|
|
54
|
+
return d
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def sanitize_item(
|
|
58
|
+
item: Any, default: Optional[Callable[..., Any]] = None
|
|
59
|
+
) -> Any:
|
|
60
|
+
"""
|
|
61
|
+
:param item: item to be sanitized
|
|
62
|
+
:param default: Optional function to be used when there is no case
|
|
63
|
+
for this type of item, default `None` it returns the item as is.
|
|
64
|
+
"""
|
|
65
|
+
rv: Union[str, list[Any]]
|
|
66
|
+
if isinstance(item, dt.date):
|
|
67
|
+
if isinstance(item, dt.datetime) and not item.tzinfo:
|
|
68
|
+
rv = item.astimezone(dt.timezone.utc).isoformat()
|
|
69
|
+
else:
|
|
70
|
+
rv = item.isoformat()
|
|
71
|
+
elif isinstance(item, list):
|
|
72
|
+
rv = [
|
|
73
|
+
sanitize_dict(e) if isinstance(e, dict) else sanitize_item(e)
|
|
74
|
+
for e in item
|
|
75
|
+
]
|
|
76
|
+
elif isinstance(item, bytes):
|
|
77
|
+
rv = base64.b64encode(item).decode('utf-8')
|
|
78
|
+
elif isinstance(item, Enum):
|
|
79
|
+
rv = item.value
|
|
80
|
+
elif hasattr(item, 'to_dict'):
|
|
81
|
+
rv = item.to_dict()
|
|
82
|
+
elif default:
|
|
83
|
+
rv = default(item)
|
|
84
|
+
else:
|
|
85
|
+
rv = item
|
|
86
|
+
return rv
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '2.1.29'
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
from pydantic import ValidationError
|
|
3
|
+
from pydantic_extra_types.phone_numbers import PhoneNumber
|
|
3
4
|
|
|
5
|
+
from cuenca_validations.types.enums import VerificationType
|
|
4
6
|
from cuenca_validations.types.requests import (
|
|
5
7
|
UserTOSAgreementRequest,
|
|
6
8
|
UserUpdateRequest,
|
|
9
|
+
VerificationRequest,
|
|
7
10
|
)
|
|
8
11
|
from cuenca_validations.typing import DictStrAny
|
|
9
12
|
|
|
@@ -53,3 +56,29 @@ def test_update_user_update_govt() -> None:
|
|
|
53
56
|
with pytest.raises(ValueError) as ex:
|
|
54
57
|
UserUpdateRequest(**govt_id)
|
|
55
58
|
assert 'uri_back must be provided for type ine' in str(ex.value)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_verification_request_normalizes_email() -> None:
|
|
62
|
+
req = VerificationRequest(
|
|
63
|
+
recipient='user+cuenca@Gmail.com',
|
|
64
|
+
type=VerificationType.email,
|
|
65
|
+
)
|
|
66
|
+
assert req.recipient == 'user@gmail.com'
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_verification_request_normalizes_phone() -> None:
|
|
70
|
+
req = VerificationRequest(
|
|
71
|
+
recipient='+116504401222',
|
|
72
|
+
type=VerificationType.phone,
|
|
73
|
+
)
|
|
74
|
+
assert req.recipient == '+16504401222'
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_user_update_request_normalizes_email() -> None:
|
|
78
|
+
req = UserUpdateRequest(email_address='user+tag@Gmail.com')
|
|
79
|
+
assert req.email_address == 'user@gmail.com'
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_user_update_request_normalizes_phone() -> None:
|
|
83
|
+
req = UserUpdateRequest(phone_number=PhoneNumber('+116504401222'))
|
|
84
|
+
assert req.phone_number == '+16504401222'
|
|
@@ -5,6 +5,7 @@ from enum import Enum
|
|
|
5
5
|
from typing import Annotated
|
|
6
6
|
|
|
7
7
|
import pytest
|
|
8
|
+
from clabe import Clabe
|
|
8
9
|
from freezegun import freeze_time
|
|
9
10
|
from pydantic import AfterValidator, BaseModel, SecretStr, ValidationError
|
|
10
11
|
from pydantic.fields import FieldInfo
|
|
@@ -16,6 +17,7 @@ from cuenca_validations.types import (
|
|
|
16
17
|
SantizedDict,
|
|
17
18
|
SessionRequest,
|
|
18
19
|
TransactionStatus,
|
|
20
|
+
UserQuery,
|
|
19
21
|
digits,
|
|
20
22
|
get_account_use_type_name,
|
|
21
23
|
get_income_type_name,
|
|
@@ -201,6 +203,50 @@ def test_card_query_exp_cvv_if_number_not_set(input_value):
|
|
|
201
203
|
CardQuery(**input_value)
|
|
202
204
|
|
|
203
205
|
|
|
206
|
+
def test_user_query_accepts_new_fields():
|
|
207
|
+
query = UserQuery(
|
|
208
|
+
clabe='646180157098510917',
|
|
209
|
+
curp='ABCD920604HDFSRN03',
|
|
210
|
+
name='Pedro Páramo',
|
|
211
|
+
)
|
|
212
|
+
assert isinstance(query.clabe, Clabe)
|
|
213
|
+
assert query.clabe == '646180157098510917'
|
|
214
|
+
assert query.curp == 'ABCD920604HDFSRN03'
|
|
215
|
+
assert query.name == 'pedro paramo'
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@pytest.mark.parametrize(
|
|
219
|
+
'raw, normalized',
|
|
220
|
+
[
|
|
221
|
+
('ab', 'ab'),
|
|
222
|
+
(' ab ', 'ab'),
|
|
223
|
+
('Raúl Andrés', 'raul andres'),
|
|
224
|
+
('raul Andres', 'raul andres'),
|
|
225
|
+
(' RAÚL ANDRÉS ', 'raul andres'),
|
|
226
|
+
('María José', 'maria jose'),
|
|
227
|
+
('ÑANDÚ', 'nandu'),
|
|
228
|
+
],
|
|
229
|
+
)
|
|
230
|
+
def test_user_query_name_normalizes(raw, normalized):
|
|
231
|
+
assert UserQuery(name=raw).name == normalized
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
@pytest.mark.parametrize(
|
|
235
|
+
'field, value',
|
|
236
|
+
[
|
|
237
|
+
('clabe', 'not-a-clabe'),
|
|
238
|
+
('curp', 'not-a-curp'),
|
|
239
|
+
('name', ''),
|
|
240
|
+
('name', ' '),
|
|
241
|
+
('name', 'a'),
|
|
242
|
+
('name', ' a '),
|
|
243
|
+
],
|
|
244
|
+
)
|
|
245
|
+
def test_user_query_rejects_invalid(field, value):
|
|
246
|
+
with pytest.raises(ValidationError):
|
|
247
|
+
UserQuery(**{field: value})
|
|
248
|
+
|
|
249
|
+
|
|
204
250
|
def test_exclude_none_in_dict():
|
|
205
251
|
request = ApiKeyUpdateRequest(user_id='US123')
|
|
206
252
|
assert request.model_dump() == dict(user_id='US123')
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from cuenca_validations.validators import (
|
|
4
|
+
normalize_email,
|
|
5
|
+
normalize_name,
|
|
6
|
+
normalize_phone_number,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.parametrize(
|
|
11
|
+
'raw, normalized',
|
|
12
|
+
[
|
|
13
|
+
('user@Yahoo.com', 'user@yahoo.com'), # uppercase domain
|
|
14
|
+
('user@iCloud.com', 'user@icloud.com'), # uppercase domain
|
|
15
|
+
('user@Gmail.com', 'user@gmail.com'), # uppercase domain
|
|
16
|
+
('user+cuenca@gmail.com', 'user@gmail.com'), # plus label
|
|
17
|
+
('user@gmail.com', 'user@gmail.com'), # already normalized
|
|
18
|
+
],
|
|
19
|
+
)
|
|
20
|
+
def test_normalize_email(raw: str, normalized: str) -> None:
|
|
21
|
+
assert normalize_email(raw) == normalized
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.mark.parametrize(
|
|
25
|
+
'raw, normalized',
|
|
26
|
+
[
|
|
27
|
+
('+116503456789', '+16503456789'), # US duplicate country code
|
|
28
|
+
('+5215512345678', '+525512345678'), # MX mobile prefix
|
|
29
|
+
('+520445512345678', '+525512345678'), # MX 044 prefix
|
|
30
|
+
('+52 (55) 1234-5678', '+525512345678'), # special characters
|
|
31
|
+
('+525512345678', '+525512345678'), # already correct MX
|
|
32
|
+
('+16503456789', '+16503456789'), # already correct US
|
|
33
|
+
],
|
|
34
|
+
)
|
|
35
|
+
def test_normalize_phone_number(raw: str, normalized: str) -> None:
|
|
36
|
+
assert normalize_phone_number(raw) == normalized
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@pytest.mark.parametrize(
|
|
40
|
+
'raw, normalized',
|
|
41
|
+
[
|
|
42
|
+
('Raúl Andrés', 'raul andres'), # accents + mixed case
|
|
43
|
+
('raul andres', 'raul andres'), # already normalized
|
|
44
|
+
('RAÚL ANDRÉS', 'raul andres'), # uppercase with accents
|
|
45
|
+
('ÑANDÚ', 'nandu'), # ñ and accent
|
|
46
|
+
('María José', 'maria jose'), # collapse internal whitespace
|
|
47
|
+
(' Raúl ', 'raul'), # trim + lowercase
|
|
48
|
+
('Nuño Garçía', 'nuno garcia'), # tilde and cedilla
|
|
49
|
+
],
|
|
50
|
+
)
|
|
51
|
+
def test_normalize_name(raw: str, normalized: str) -> None:
|
|
52
|
+
assert normalize_name(raw) == normalized
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import base64
|
|
2
|
-
import datetime as dt
|
|
3
|
-
from enum import Enum
|
|
4
|
-
from typing import Any, Callable, Optional, Union
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def sanitize_dict(d: dict) -> dict:
|
|
8
|
-
for k, v in d.items():
|
|
9
|
-
d[k] = sanitize_item(v)
|
|
10
|
-
return d
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def sanitize_item(
|
|
14
|
-
item: Any, default: Optional[Callable[..., Any]] = None
|
|
15
|
-
) -> Any:
|
|
16
|
-
"""
|
|
17
|
-
:param item: item to be sanitized
|
|
18
|
-
:param default: Optional function to be used when there is no case
|
|
19
|
-
for this type of item, default `None` it returns the item as is.
|
|
20
|
-
"""
|
|
21
|
-
rv: Union[str, list[Any]]
|
|
22
|
-
if isinstance(item, dt.date):
|
|
23
|
-
if isinstance(item, dt.datetime) and not item.tzinfo:
|
|
24
|
-
rv = item.astimezone(dt.timezone.utc).isoformat()
|
|
25
|
-
else:
|
|
26
|
-
rv = item.isoformat()
|
|
27
|
-
elif isinstance(item, list):
|
|
28
|
-
rv = [
|
|
29
|
-
sanitize_dict(e) if isinstance(e, dict) else sanitize_item(e)
|
|
30
|
-
for e in item
|
|
31
|
-
]
|
|
32
|
-
elif isinstance(item, bytes):
|
|
33
|
-
rv = base64.b64encode(item).decode('utf-8')
|
|
34
|
-
elif isinstance(item, Enum):
|
|
35
|
-
rv = item.value
|
|
36
|
-
elif hasattr(item, 'to_dict'):
|
|
37
|
-
rv = item.to_dict()
|
|
38
|
-
elif default:
|
|
39
|
-
rv = default(item)
|
|
40
|
-
else:
|
|
41
|
-
rv = item
|
|
42
|
-
return rv
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '2.1.27'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations/types/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations/types/identities.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations.egg-info/requires.txt
RENAMED
|
File without changes
|
{cuenca_validations-2.1.27 → cuenca_validations-2.1.29}/cuenca_validations.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|