Skip to content

Commit

Permalink
address feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
David Montague committed Jun 15, 2019
1 parent 93ffa47 commit 93afcd9
Show file tree
Hide file tree
Showing 8 changed files with 40 additions and 29 deletions.
14 changes: 7 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ install:
- pip freeze

script:
# test without cython but with ujson and email-validator
# test without cython but with ujson, email-validator, and typing-extensions
- python -c "import sys, pydantic; print('compiled:', pydantic.compiled); sys.exit(1 if pydantic.compiled else 0)"
- make test

Expand All @@ -40,7 +40,7 @@ jobs:
python: 3.6
name: 'Cython: 3.6'
script:
# test with cython, ujson and email-validator
# test with cython, ujson, email-validator, and typing-extensions
- make build-cython-trace
- python -c "import sys, pydantic; print('compiled:', pydantic.compiled); sys.exit(0 if pydantic.compiled else 1)"
- make test
Expand All @@ -50,7 +50,7 @@ jobs:
python: 3.7
name: 'Cython: 3.7'
script:
# test with cython, ujson and email-validator
# test with cython, ujson, email-validator, and typing-extensions
- make build-cython-trace
- python -c "import sys, pydantic; print('compiled:', pydantic.compiled); sys.exit(0 if pydantic.compiled else 1)"
- make test
Expand All @@ -61,17 +61,17 @@ jobs:
python: 3.6
name: 'Without Deps 3.6'
script:
# test without cython, ujson and email-validator
- pip uninstall -y ujson email-validator
# test without cython, ujson, email-validator, and typing-extensions
- pip uninstall -y ujson email-validator typing-extensions
- make test
env:
- 'DEPS=no'
- stage: test
python: 3.7
name: 'Without Deps 3.7'
script:
# test without cython, ujson and email-validator
- pip uninstall -y ujson email-validator cython
# test without cython, ujson, email-validator, and typing-extensions
- pip uninstall -y ujson email-validator cython typing-extensions
- make test
env:
- 'DEPS=no'
Expand Down
6 changes: 5 additions & 1 deletion pydantic/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ class NoneIsAllowedError(PydanticTypeError):

class WrongConstantError(PydanticValueError):
code = 'const'
msg_template = 'unexpected constant value; permitted values: {allowed!r}'

def __str__(self):
given = self.ctx['given']
permitted = ', '.join(repr(v) for v in self.ctx['permitted'])
return f'unexpected value; given: {given!r}; permitted: {permitted}'


class BytesError(PydanticTypeError):
Expand Down
18 changes: 11 additions & 7 deletions pydantic/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,18 @@
from .class_validators import Validator, make_generic_validator
from .error_wrappers import ErrorWrapper
from .types import Json, JsonWrapper
from .utils import AnyCallable, AnyType, Callable, ForwardRef, display_as_type, lenient_issubclass, sequence_like
from .utils import (
AnyCallable,
AnyType,
Callable,
ForwardRef,
Literal,
display_as_type,
lenient_issubclass,
sequence_like,
)
from .validators import NoneType, constant_validator, dict_validator, find_validators

try:
from typing_extensions import Literal
except ImportError:
Literal = object() # type: ignore

Required: Any = Ellipsis

if TYPE_CHECKING: # pragma: no cover
Expand Down Expand Up @@ -192,7 +196,7 @@ def _populate_sub_fields(self) -> None: # noqa: C901 (ignore complexity)
return
if origin is Callable:
return
if origin is Literal:
if Literal is not None and origin is Literal:
return
if origin is Union:
types_ = []
Expand Down
4 changes: 2 additions & 2 deletions pydantic/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
try:
from typing_extensions import Literal
except ImportError:
Literal = object() # type: ignore
Literal = None # type: ignore

try:
import email_validator
Expand Down Expand Up @@ -291,7 +291,7 @@ def is_callable_type(type_: AnyType) -> bool:


def is_literal_type(type_: AnyType) -> bool:
return getattr(type_, '__origin__', None) is Literal
return Literal is not None and getattr(type_, '__origin__', None)


def _check_classvar(v: AnyType) -> bool:
Expand Down
8 changes: 5 additions & 3 deletions pydantic/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ def constant_validator(v: 'Any', field: 'Field') -> 'Any':
Schema.
"""
if v != field.default:
raise errors.WrongConstantError(given=v, allowed=[field.default])
raise errors.WrongConstantError(given=v, permitted=[field.default])

return v

Expand Down Expand Up @@ -344,9 +344,11 @@ def callable_validator(v: Any) -> AnyCallable:


def make_literal_validator(allowed_choices: Tuple[Any, ...]) -> Callable[[Any], Any]:
allowed_choices_set = set(allowed_choices)

def literal_validator(v: Any) -> Any:
if v not in allowed_choices:
raise errors.WrongConstantError(allowed=list(allowed_choices))
if v not in allowed_choices_set:
raise errors.WrongConstantError(given=v, permitted=list(allowed_choices))
return v

return literal_validator
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def extra(self):
extras_require={
'ujson': ['ujson>=1.35'],
'email': ['email-validator>=1.0.3'],
'typing_extensions': ['typing_extensions']
'typing_extensions': ['typing-extensions>=3.7.2']
},
ext_modules=ext_modules,
)
4 changes: 2 additions & 2 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,9 +391,9 @@ class Model(BaseModel):
assert exc_info.value.errors() == [
{
'loc': ('a',),
'msg': 'unexpected constant value; permitted values: [3]',
'msg': 'unexpected value; given: 4; permitted: 3',
'type': 'value_error.const',
'ctx': {'given': 4, 'allowed': [3]},
'ctx': {'given': 4, 'permitted': [3]},
}
]

Expand Down
13 changes: 7 additions & 6 deletions tests/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
except ImportError:
typing_extensions = None


class ConBytesModel(BaseModel):
v: conbytes(max_length=10) = b'foobar'

Expand Down Expand Up @@ -1575,25 +1576,25 @@ class Model(BaseModel):
@pytest.mark.skipif(not typing_extensions, reason='typing_extensions not installed')
def test_literal_single():
class Model(BaseModel):
a: Literal['a']
a: typing_extensions.Literal['a']

Model(a='a')
with pytest.raises(ValidationError) as exc_info:
Model(a='b')
assert exc_info.value.errors() == [
{
'loc': ('a',),
'msg': 'unexpected constant value; permitted values: [\'a\']',
'msg': 'unexpected value; given: \'b\'; permitted: \'a\'',
'type': 'value_error.const',
'ctx': {'allowed': ['a']},
'ctx': {'given': 'b', 'permitted': ['a']},
}
]


@pytest.mark.skipif(not typing_extensions, reason='typing_extensions not installed')
def test_literal_multiple():
class Model(BaseModel):
a_or_b: Literal['a', 'b']
a_or_b: typing_extensions.Literal['a', 'b']

Model(a_or_b='a')
Model(a_or_b='b')
Expand All @@ -1602,8 +1603,8 @@ class Model(BaseModel):
assert exc_info.value.errors() == [
{
'loc': ('a_or_b',),
'msg': 'unexpected constant value; permitted values: [\'a\', \'b\']',
'msg': 'unexpected value; given: \'c\'; permitted: \'a\', \'b\'',
'type': 'value_error.const',
'ctx': {'allowed': ['a', 'b']},
'ctx': {'given': 'c', 'permitted': ['a', 'b']},
}
]

0 comments on commit 93afcd9

Please sign in to comment.