Repository: pydantic/pydantic

Issue:
Pydantic does not seem to have a validator for `NoneType`
I encountered this while working on #2017:

```py
from typing import List
from pydantic import BaseModel, Json

class JsonDemo(BaseModel):
    # I'd expect this to work like typing.List[None], but at definition-time it raises
    # RuntimeError: error checking inheritance of None (type: NoneType)
    none: Json[None]
JsonDemo(none="null")


class JsonWorkaround(BaseModel):
    # The workaround of using type(None) also fails at definition-time, raising
    # RuntimeError: no validator found for <class 'NoneType'>, see `arbitrary_types_allowed` in Config
    # The same error is apparent for List[None], so I think it's a more general bug
    json_null: Json[type(None)]
    list_of_none: List[None]
```

I would expect both `Json[None]` and `List[None]` to work, accepting in respectively `"null"` or a list (or sequence) where all the elements are `None` - casting need not be supported because `None` is a singleton.


Comments:
I think we could have a `NoneType` which enforces a value to be `None`. But I think if we made `foo: None` a valid way of enforcing a value is None it would make a lot of logic more complicated and result in lots of easy traps for people to fall into.
I agree that we should not allow `foo: None` as a type constraint, but we could allow `foo: type(None)` or `foo: pydantic.typing.NoneType`. This would be effectively equivalent to `Literal[None]`.
Mypy, along with other type checkers, already treat `foo: None` as equivalent to `foo: Literal[None]` or `foo: NoneType`.  My proposal is that Pydantic match this behaviour, even though it would require some extra logic for the sake of consistency with the rest of the ecosystem.

`foo: type(None)` makes Mypy fail with `error: Invalid type comment or annotation. Suggestion: use type[...] instead of type(...)`, so while Pydantic should probably allow it at runtime we'd have to recommend assigning `type(None)` to a variable and using that in annotations.
According to https://www.python.org/dev/peps/pep-0484/#using-none, `None` should be considered equivalent to `type(None)`, so this is definitely a bug.

Patch:
diff --git a/docs/build/schema_mapping.py b/docs/build/schema_mapping.py
--- a/docs/build/schema_mapping.py
+++ b/docs/build/schema_mapping.py
@@ -11,6 +11,13 @@
 from pathlib import Path
 
 table = [
+    [
+        'None',
+        'null',
+        '',
+        'JSON Schema Core',
+        'Same for `type(None)` or `Literal[None]`'
+    ],
     [
         'bool',
         'boolean',
diff --git a/pydantic/errors.py b/pydantic/errors.py
--- a/pydantic/errors.py
+++ b/pydantic/errors.py
@@ -17,6 +17,7 @@
     'NoneIsNotAllowedError',
     'NoneIsAllowedError',
     'WrongConstantError',
+    'NotNoneError',
     'BoolError',
     'BytesError',
     'DictError',
@@ -160,6 +161,11 @@ def __str__(self) -> str:
         return f'unexpected value; permitted: {permitted}'
 
 
+class NotNoneError(PydanticTypeError):
+    code = 'not_none'
+    msg_template = 'value is not None'
+
+
 class BoolError(PydanticTypeError):
     msg_template = 'value could not be parsed to a boolean'
 
diff --git a/pydantic/fields.py b/pydantic/fields.py
--- a/pydantic/fields.py
+++ b/pydantic/fields.py
@@ -28,6 +28,7 @@
 from .errors import NoneIsNotAllowedError
 from .types import Json, JsonWrapper
 from .typing import (
+    NONE_TYPES,
     Callable,
     ForwardRef,
     NoArgAnyCallable,
@@ -368,7 +369,7 @@ def _set_default_and_type(self) -> None:
         when we want to validate the default value i.e. when `validate_all` is set to True.
         """
         if self.default_factory is not None:
-            if self.type_ is None:
+            if self.type_ is Undefined:
                 raise errors_.ConfigError(
                     f'you need to set the type of field {self.name!r} when using `default_factory`'
                 )
@@ -377,11 +378,11 @@ def _set_default_and_type(self) -> None:
 
         default_value = self.get_default()
 
-        if default_value is not None and self.type_ is None:
+        if default_value is not None and self.type_ is Undefined:
             self.type_ = default_value.__class__
             self.outer_type_ = self.type_
 
-        if self.type_ is None:
+        if self.type_ is Undefined:
             raise errors_.ConfigError(f'unable to infer type for attribute "{self.name}"')
 
         if self.required is False and default_value is None:
@@ -571,7 +572,10 @@ def validate(
                 return v, errors
 
         if v is None:
-            if self.allow_none:
+            if self.type_ in NONE_TYPES:
+                # keep validating
+                pass
+            elif self.allow_none:
                 if self.post_validators:
                     return self._apply_validators(v, values, loc, cls, self.post_validators)
                 else:
@@ -753,12 +757,6 @@ def _apply_validators(
                 return v, ErrorWrapper(exc, loc)
         return v, None
 
-    def include_in_schema(self) -> bool:
-        """
-        False if this is a simple field just allowing None as used in Unions/Optional.
-        """
-        return self.type_ != NoneType
-
     def is_complex(self) -> bool:
         """
         Whether the field is "complex" eg. env variables should be parsed as JSON.
diff --git a/pydantic/main.py b/pydantic/main.py
--- a/pydantic/main.py
+++ b/pydantic/main.py
@@ -285,7 +285,7 @@ def __new__(mcs, name, bases, namespace, **kwargs):  # noqa C901
                     inferred = ModelField.infer(
                         name=var_name,
                         value=value,
-                        annotation=annotations.get(var_name),
+                        annotation=annotations.get(var_name, Undefined),
                         class_validators=vg.get_validators(var_name),
                         config=config,
                     )
diff --git a/pydantic/schema.py b/pydantic/schema.py
--- a/pydantic/schema.py
+++ b/pydantic/schema.py
@@ -57,7 +57,16 @@
     conset,
     constr,
 )
-from .typing import ForwardRef, Literal, get_args, get_origin, is_callable_type, is_literal_type, literal_values
+from .typing import (
+    NONE_TYPES,
+    ForwardRef,
+    Literal,
+    get_args,
+    get_origin,
+    is_callable_type,
+    is_literal_type,
+    literal_values,
+)
 from .utils import ROOT_KEY, get_model, lenient_issubclass, sequence_like
 
 if TYPE_CHECKING:
@@ -640,7 +649,6 @@ def field_singleton_sub_fields_schema(
     """
     definitions = {}
     nested_models: Set[str] = set()
-    sub_fields = [sf for sf in sub_fields if sf.include_in_schema()]
     if len(sub_fields) == 1:
         return field_type_schema(
             sub_fields[0],
@@ -759,6 +767,8 @@ def field_singleton_schema(  # noqa: C901 (ignore complexity)
         )
     if field.type_ is Any or field.type_.__class__ == TypeVar:
         return {}, definitions, nested_models  # no restrictions
+    if field.type_ in NONE_TYPES:
+        return {'type': 'null'}, definitions, nested_models
     if is_callable_type(field.type_):
         raise SkipField(f'Callable {field.name} was excluded from schema since JSON schema has no equivalent type.')
     f_schema: Dict[str, Any] = {}
diff --git a/pydantic/typing.py b/pydantic/typing.py
--- a/pydantic/typing.py
+++ b/pydantic/typing.py
@@ -149,6 +149,7 @@ def get_args(tp: Type[Any]) -> Tuple[Any, ...]:
     'AnyCallable',
     'NoArgAnyCallable',
     'NoneType',
+    'NONE_TYPES',
     'display_as_type',
     'resolve_annotations',
     'is_callable_type',
@@ -176,6 +177,9 @@ def get_args(tp: Type[Any]) -> Tuple[Any, ...]:
 
 
 NoneType = None.__class__
+NONE_TYPES: Set[Any] = {None, NoneType}
+if Literal:
+    NONE_TYPES.add(Literal[None])
 
 
 def display_as_type(v: Type[Any]) -> str:
diff --git a/pydantic/validators.py b/pydantic/validators.py
--- a/pydantic/validators.py
+++ b/pydantic/validators.py
@@ -27,8 +27,10 @@
 from . import errors
 from .datetime_parse import parse_date, parse_datetime, parse_duration, parse_time
 from .typing import (
+    NONE_TYPES,
     AnyCallable,
     ForwardRef,
+    Literal,
     all_literal_values,
     display_as_type,
     get_class,
@@ -511,6 +513,12 @@ def any_class_validator(v: Any) -> Type[T]:
     raise errors.ClassError()
 
 
+def none_validator(v: Any) -> 'Literal[None]':
+    if v is None:
+        return v
+    raise errors.NotNoneError()
+
+
 def pattern_validator(v: Any) -> Pattern[str]:
     if isinstance(v, Pattern):
         return v
@@ -589,6 +597,9 @@ def find_validators(  # noqa: C901 (ignore complexity)
     type_type = type_.__class__
     if type_type == ForwardRef or type_type == TypeVar:
         return
+    if type_ in NONE_TYPES:
+        yield none_validator
+        return
     if type_ is Pattern:
         yield pattern_validator
         return


Test patch:
diff --git a/tests/test_types.py b/tests/test_types.py
--- a/tests/test_types.py
+++ b/tests/test_types.py
@@ -69,17 +69,13 @@
     errors,
     validator,
 )
+from pydantic.typing import Literal, NoneType
 
 try:
     import email_validator
 except ImportError:
     email_validator = None
 
-try:
-    import typing_extensions
-except ImportError:
-    typing_extensions = None
-
 
 class ConBytesModel(BaseModel):
     v: conbytes(max_length=10) = b'foobar'
@@ -2396,10 +2392,10 @@ class Model(BaseModel):
     ]
 
 
-@pytest.mark.skipif(not typing_extensions, reason='typing_extensions not installed')
+@pytest.mark.skipif(not Literal, reason='typing_extensions not installed')
 def test_literal_single():
     class Model(BaseModel):
-        a: typing_extensions.Literal['a']
+        a: Literal['a']
 
     Model(a='a')
     with pytest.raises(ValidationError) as exc_info:
@@ -2414,10 +2410,10 @@ class Model(BaseModel):
     ]
 
 
-@pytest.mark.skipif(not typing_extensions, reason='typing_extensions not installed')
+@pytest.mark.skipif(not Literal, reason='typing_extensions not installed')
 def test_literal_multiple():
     class Model(BaseModel):
-        a_or_b: typing_extensions.Literal['a', 'b']
+        a_or_b: Literal['a', 'b']
 
     Model(a_or_b='a')
     Model(a_or_b='b')
@@ -2609,3 +2605,59 @@ class Model(BaseModel):
         v: Deque[int]
 
     assert Model(v=deque((1, 2, 3))).json() == '{"v": [1, 2, 3]}'
+
+
+none_value_type_cases = (None, type(None), NoneType)
+if Literal:
+    none_value_type_cases += (Literal[None],)
+
+
+@pytest.mark.parametrize('value_type', none_value_type_cases)
+def test_none(value_type):
+    class Model(BaseModel):
+        my_none: value_type
+        my_none_list: List[value_type]
+        my_none_dict: Dict[str, value_type]
+        my_json_none: Json[value_type]
+
+    Model(
+        my_none=None,
+        my_none_list=[None] * 3,
+        my_none_dict={'a': None, 'b': None},
+        my_json_none='null',
+    )
+
+    assert Model.schema() == {
+        'title': 'Model',
+        'type': 'object',
+        'properties': {
+            'my_none': {'title': 'My None', 'type': 'null'},
+            'my_none_list': {
+                'title': 'My None List',
+                'type': 'array',
+                'items': {'type': 'null'},
+            },
+            'my_none_dict': {
+                'title': 'My None Dict',
+                'type': 'object',
+                'additionalProperties': {'type': 'null'},
+            },
+            'my_json_none': {'title': 'My Json None', 'type': 'null'},
+        },
+        'required': ['my_none', 'my_none_list', 'my_none_dict', 'my_json_none'],
+    }
+
+    with pytest.raises(ValidationError) as exc_info:
+        Model(
+            my_none='qwe',
+            my_none_list=[1, None, 'qwe'],
+            my_none_dict={'a': 1, 'b': None},
+            my_json_none='"a"',
+        )
+    assert exc_info.value.errors() == [
+        {'loc': ('my_none',), 'msg': 'value is not None', 'type': 'type_error.not_none'},
+        {'loc': ('my_none_list', 0), 'msg': 'value is not None', 'type': 'type_error.not_none'},
+        {'loc': ('my_none_list', 2), 'msg': 'value is not None', 'type': 'type_error.not_none'},
+        {'loc': ('my_none_dict', 'a'), 'msg': 'value is not None', 'type': 'type_error.not_none'},
+        {'loc': ('my_json_none',), 'msg': 'value is not None', 'type': 'type_error.not_none'},
+    ]