mirror of
https://github.com/kennethreitz/pydantic.git
synced 2026-06-05 23:00:18 +00:00
Add to upper function for strings and bytes (#4165)
* feat: add to upper function for strings and bytes * docs(changes): add message for change * fix: add constr upper on types * fix: add constr upper on types * feat: add examples and doc usage * test: add test to upper for types * chore: apply suggestions from code review Co-authored-by: Hasan Ramezani <hasan.r67@gmail.com> * chore(docs): reorder `anystr_upper` to under `anystr_lower` * fix(test): adjust parametrizes to constrained bytes upper * refactor: use pytest parametrize for unify test constrained str upper * refactor: use pytest parametrize for unify test constrained str lower * refactor(test): use pytest parametrize for unify test any str upper * refactor(test): use pytest parametrize for unify test any str lower * refactor(test): use pytest parametrize for unify test constrained bytes lower * refactor(test): use pytest parametrize for unify test any str strip whitespace * refactor(test): change test signatures to improve readability Co-authored-by: Hasan Ramezani <hasan.r67@gmail.com>
This commit is contained in:
@@ -0,0 +1 @@
|
||||
Add `Config.anystr_upper` and `to_upper` kwarg to constr and conbytes.
|
||||
@@ -22,10 +22,12 @@ from pydantic import (
|
||||
|
||||
|
||||
class Model(BaseModel):
|
||||
upper_bytes: conbytes(to_upper=True)
|
||||
lower_bytes: conbytes(to_lower=True)
|
||||
short_bytes: conbytes(min_length=2, max_length=10)
|
||||
strip_bytes: conbytes(strip_whitespace=True)
|
||||
|
||||
upper_str: constr(to_upper=True)
|
||||
lower_str: constr(to_lower=True)
|
||||
short_str: constr(min_length=2, max_length=10)
|
||||
regex_str: constr(regex=r'^apple (pie|tart|sandwich)$')
|
||||
|
||||
@@ -25,6 +25,9 @@ _(This script is complete, it should run "as is")_
|
||||
**`anystr_strip_whitespace`**
|
||||
: whether to strip leading and trailing whitespace for str & byte types (default: `False`)
|
||||
|
||||
**`anystr_upper`**
|
||||
: whether to make all characters uppercase for str & byte types (default: `False`)
|
||||
|
||||
**`anystr_lower`**
|
||||
: whether to make all characters lowercase for str & byte types (default: `False`)
|
||||
|
||||
|
||||
@@ -910,6 +910,7 @@ The following arguments are available when using the `condecimal` type function
|
||||
The following arguments are available when using the `constr` type function
|
||||
|
||||
- `strip_whitespace: bool = False`: removes leading and trailing whitespace
|
||||
- `to_upper: bool = False`: turns all characters to uppercase
|
||||
- `to_lower: bool = False`: turns all characters to lowercase
|
||||
- `strict: bool = False`: controls type coercion
|
||||
- `min_length: int = None`: minimum length of the string
|
||||
@@ -921,6 +922,7 @@ The following arguments are available when using the `constr` type function
|
||||
The following arguments are available when using the `conbytes` type function
|
||||
|
||||
- `strip_whitespace: bool = False`: removes leading and trailing whitespace
|
||||
- `to_upper: bool = False`: turns all characters to uppercase
|
||||
- `to_lower: bool = False`: turns all characters to lowercase
|
||||
- `min_length: int = None`: minimum length of the byte string
|
||||
- `max_length: int = None`: maximum length of the byte string
|
||||
|
||||
@@ -80,6 +80,7 @@ else:
|
||||
class BaseConfig:
|
||||
title: Optional[str] = None
|
||||
anystr_lower: bool = False
|
||||
anystr_upper: bool = False
|
||||
anystr_strip_whitespace: bool = False
|
||||
min_anystr_length: int = 0
|
||||
max_anystr_length: Optional[int] = None
|
||||
|
||||
@@ -35,6 +35,7 @@ from .validators import (
|
||||
constr_length_validator,
|
||||
constr_lower,
|
||||
constr_strip_whitespace,
|
||||
constr_upper,
|
||||
decimal_validator,
|
||||
float_validator,
|
||||
frozenset_validator,
|
||||
@@ -328,6 +329,7 @@ else:
|
||||
|
||||
class ConstrainedBytes(bytes):
|
||||
strip_whitespace = False
|
||||
to_upper = False
|
||||
to_lower = False
|
||||
min_length: OptionalInt = None
|
||||
max_length: OptionalInt = None
|
||||
@@ -341,6 +343,7 @@ class ConstrainedBytes(bytes):
|
||||
def __get_validators__(cls) -> 'CallableGenerator':
|
||||
yield strict_bytes_validator if cls.strict else bytes_validator
|
||||
yield constr_strip_whitespace
|
||||
yield constr_upper
|
||||
yield constr_lower
|
||||
yield constr_length_validator
|
||||
|
||||
@@ -348,6 +351,7 @@ class ConstrainedBytes(bytes):
|
||||
def conbytes(
|
||||
*,
|
||||
strip_whitespace: bool = False,
|
||||
to_upper: bool = False,
|
||||
to_lower: bool = False,
|
||||
min_length: int = None,
|
||||
max_length: int = None,
|
||||
@@ -356,6 +360,7 @@ def conbytes(
|
||||
# use kwargs then define conf in a dict to aid with IDE type hinting
|
||||
namespace = dict(
|
||||
strip_whitespace=strip_whitespace,
|
||||
to_upper=to_upper,
|
||||
to_lower=to_lower,
|
||||
min_length=min_length,
|
||||
max_length=max_length,
|
||||
@@ -377,6 +382,7 @@ else:
|
||||
|
||||
class ConstrainedStr(str):
|
||||
strip_whitespace = False
|
||||
to_upper = False
|
||||
to_lower = False
|
||||
min_length: OptionalInt = None
|
||||
max_length: OptionalInt = None
|
||||
@@ -397,6 +403,7 @@ class ConstrainedStr(str):
|
||||
def __get_validators__(cls) -> 'CallableGenerator':
|
||||
yield strict_str_validator if cls.strict else str_validator
|
||||
yield constr_strip_whitespace
|
||||
yield constr_upper
|
||||
yield constr_lower
|
||||
yield constr_length_validator
|
||||
yield cls.validate
|
||||
@@ -416,6 +423,7 @@ class ConstrainedStr(str):
|
||||
def constr(
|
||||
*,
|
||||
strip_whitespace: bool = False,
|
||||
to_upper: bool = False,
|
||||
to_lower: bool = False,
|
||||
strict: bool = False,
|
||||
min_length: int = None,
|
||||
@@ -426,6 +434,7 @@ def constr(
|
||||
# use kwargs then define conf in a dict to aid with IDE type hinting
|
||||
namespace = dict(
|
||||
strip_whitespace=strip_whitespace,
|
||||
to_upper=to_upper,
|
||||
to_lower=to_lower,
|
||||
strict=strict,
|
||||
min_length=min_length,
|
||||
|
||||
@@ -206,6 +206,10 @@ def anystr_strip_whitespace(v: 'StrBytes') -> 'StrBytes':
|
||||
return v.strip()
|
||||
|
||||
|
||||
def anystr_upper(v: 'StrBytes') -> 'StrBytes':
|
||||
return v.upper()
|
||||
|
||||
|
||||
def anystr_lower(v: 'StrBytes') -> 'StrBytes':
|
||||
return v.lower()
|
||||
|
||||
@@ -488,6 +492,14 @@ def constr_strip_whitespace(v: 'StrBytes', field: 'ModelField', config: 'BaseCon
|
||||
return v
|
||||
|
||||
|
||||
def constr_upper(v: 'StrBytes', field: 'ModelField', config: 'BaseConfig') -> 'StrBytes':
|
||||
upper = field.type_.to_upper or config.anystr_upper
|
||||
if upper:
|
||||
v = v.upper()
|
||||
|
||||
return v
|
||||
|
||||
|
||||
def constr_lower(v: 'StrBytes', field: 'ModelField', config: 'BaseConfig') -> 'StrBytes':
|
||||
lower = field.type_.to_lower or config.anystr_lower
|
||||
if lower:
|
||||
@@ -614,6 +626,7 @@ _VALIDATORS: List[Tuple[Type[Any], List[Any]]] = [
|
||||
[
|
||||
str_validator,
|
||||
IfConfig(anystr_strip_whitespace, 'anystr_strip_whitespace'),
|
||||
IfConfig(anystr_upper, 'anystr_upper'),
|
||||
IfConfig(anystr_lower, 'anystr_lower'),
|
||||
IfConfig(anystr_length_validator, 'min_anystr_length', 'max_anystr_length'),
|
||||
],
|
||||
@@ -623,6 +636,7 @@ _VALIDATORS: List[Tuple[Type[Any], List[Any]]] = [
|
||||
[
|
||||
bytes_validator,
|
||||
IfConfig(anystr_strip_whitespace, 'anystr_strip_whitespace'),
|
||||
IfConfig(anystr_upper, 'anystr_upper'),
|
||||
IfConfig(anystr_lower, 'anystr_lower'),
|
||||
IfConfig(anystr_length_validator, 'min_anystr_length', 'max_anystr_length'),
|
||||
],
|
||||
|
||||
+75
-45
@@ -113,20 +113,34 @@ def test_constrained_bytes_too_long():
|
||||
]
|
||||
|
||||
|
||||
def test_constrained_bytes_lower_enabled():
|
||||
@pytest.mark.parametrize(
|
||||
'to_upper, value, result',
|
||||
[
|
||||
(True, b'abcd', b'ABCD'),
|
||||
(False, b'aBcD', b'aBcD'),
|
||||
],
|
||||
)
|
||||
def test_constrained_bytes_upper(to_upper, value, result):
|
||||
class Model(BaseModel):
|
||||
v: conbytes(to_lower=True)
|
||||
v: conbytes(to_upper=to_upper)
|
||||
|
||||
m = Model(v=b'ABCD')
|
||||
assert m.v == b'abcd'
|
||||
m = Model(v=value)
|
||||
assert m.v == result
|
||||
|
||||
|
||||
def test_constrained_bytes_lower_disabled():
|
||||
@pytest.mark.parametrize(
|
||||
'to_lower, value, result',
|
||||
[
|
||||
(True, b'ABCD', b'abcd'),
|
||||
(False, b'ABCD', b'ABCD'),
|
||||
],
|
||||
)
|
||||
def test_constrained_bytes_lower(to_lower, value, result):
|
||||
class Model(BaseModel):
|
||||
v: conbytes(to_lower=False)
|
||||
v: conbytes(to_lower=to_lower)
|
||||
|
||||
m = Model(v=b'ABCD')
|
||||
assert m.v == b'ABCD'
|
||||
m = Model(v=value)
|
||||
assert m.v == result
|
||||
|
||||
|
||||
def test_constrained_bytes_strict_true():
|
||||
@@ -699,20 +713,34 @@ def test_constrained_str_too_long():
|
||||
]
|
||||
|
||||
|
||||
def test_constrained_str_lower_enabled():
|
||||
@pytest.mark.parametrize(
|
||||
'to_upper, value, result',
|
||||
[
|
||||
(True, 'abcd', 'ABCD'),
|
||||
(False, 'aBcD', 'aBcD'),
|
||||
],
|
||||
)
|
||||
def test_constrained_str_upper(to_upper, value, result):
|
||||
class Model(BaseModel):
|
||||
v: constr(to_lower=True)
|
||||
v: constr(to_upper=to_upper)
|
||||
|
||||
m = Model(v='ABCD')
|
||||
assert m.v == 'abcd'
|
||||
m = Model(v=value)
|
||||
assert m.v == result
|
||||
|
||||
|
||||
def test_constrained_str_lower_disabled():
|
||||
@pytest.mark.parametrize(
|
||||
'to_lower, value, result',
|
||||
[
|
||||
(True, 'ABCD', 'abcd'),
|
||||
(False, 'ABCD', 'ABCD'),
|
||||
],
|
||||
)
|
||||
def test_constrained_str_lower(to_lower, value, result):
|
||||
class Model(BaseModel):
|
||||
v: constr(to_lower=False)
|
||||
v: constr(to_lower=to_lower)
|
||||
|
||||
m = Model(v='ABCD')
|
||||
assert m.v == 'ABCD'
|
||||
m = Model(v=value)
|
||||
assert m.v == result
|
||||
|
||||
|
||||
def test_constrained_str_max_length_0():
|
||||
@@ -1784,58 +1812,60 @@ def test_uuid_validation():
|
||||
]
|
||||
|
||||
|
||||
def test_anystr_strip_whitespace_enabled():
|
||||
@pytest.mark.parametrize(
|
||||
'enabled, str_check, bytes_check, result_str_check, result_bytes_check',
|
||||
[
|
||||
(True, ' 123 ', b' 456 ', '123', b'456'),
|
||||
(False, ' 123 ', b' 456 ', ' 123 ', b' 456 '),
|
||||
],
|
||||
)
|
||||
def test_anystr_strip_whitespace(enabled, str_check, bytes_check, result_str_check, result_bytes_check):
|
||||
class Model(BaseModel):
|
||||
str_check: str
|
||||
bytes_check: bytes
|
||||
|
||||
class Config:
|
||||
anystr_strip_whitespace = True
|
||||
anystr_strip_whitespace = enabled
|
||||
|
||||
m = Model(str_check=' 123 ', bytes_check=b' 456 ')
|
||||
assert m.str_check == '123'
|
||||
assert m.bytes_check == b'456'
|
||||
m = Model(str_check=str_check, bytes_check=bytes_check)
|
||||
assert m.str_check == result_str_check
|
||||
assert m.bytes_check == result_bytes_check
|
||||
|
||||
|
||||
def test_anystr_strip_whitespace_disabled():
|
||||
@pytest.mark.parametrize(
|
||||
'enabled, str_check, bytes_check, result_str_check, result_bytes_check',
|
||||
[(True, 'ABCDefG', b'abCD1Fg', 'ABCDEFG', b'ABCD1FG'), (False, 'ABCDefG', b'abCD1Fg', 'ABCDefG', b'abCD1Fg')],
|
||||
)
|
||||
def test_anystr_upper(enabled, str_check, bytes_check, result_str_check, result_bytes_check):
|
||||
class Model(BaseModel):
|
||||
str_check: str
|
||||
bytes_check: bytes
|
||||
|
||||
class Config:
|
||||
anystr_strip_whitespace = False
|
||||
anystr_upper = enabled
|
||||
|
||||
m = Model(str_check=' 123 ', bytes_check=b' 456 ')
|
||||
assert m.str_check == ' 123 '
|
||||
assert m.bytes_check == b' 456 '
|
||||
m = Model(str_check=str_check, bytes_check=bytes_check)
|
||||
|
||||
assert m.str_check == result_str_check
|
||||
assert m.bytes_check == result_bytes_check
|
||||
|
||||
|
||||
def test_anystr_lower_enabled():
|
||||
@pytest.mark.parametrize(
|
||||
'enabled, str_check, bytes_check, result_str_check, result_bytes_check',
|
||||
[(True, 'ABCDefG', b'abCD1Fg', 'abcdefg', b'abcd1fg'), (False, 'ABCDefG', b'abCD1Fg', 'ABCDefG', b'abCD1Fg')],
|
||||
)
|
||||
def test_anystr_lower(enabled, str_check, bytes_check, result_str_check, result_bytes_check):
|
||||
class Model(BaseModel):
|
||||
str_check: str
|
||||
bytes_check: bytes
|
||||
|
||||
class Config:
|
||||
anystr_lower = True
|
||||
anystr_lower = enabled
|
||||
|
||||
m = Model(str_check='ABCDefG', bytes_check=b'abCD1Fg')
|
||||
m = Model(str_check=str_check, bytes_check=bytes_check)
|
||||
|
||||
assert m.str_check == 'abcdefg'
|
||||
assert m.bytes_check == b'abcd1fg'
|
||||
|
||||
|
||||
def test_anystr_lower_disabled():
|
||||
class Model(BaseModel):
|
||||
str_check: str
|
||||
bytes_check: bytes
|
||||
|
||||
class Config:
|
||||
anystr_lower = False
|
||||
|
||||
m = Model(str_check='ABCDefG', bytes_check=b'abCD1Fg')
|
||||
|
||||
assert m.str_check == 'ABCDefG'
|
||||
assert m.bytes_check == b'abCD1Fg'
|
||||
assert m.str_check == result_str_check
|
||||
assert m.bytes_check == result_bytes_check
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
Reference in New Issue
Block a user