Repository: pydantic/pydantic

Issue:
NewType of List, Optional, etc. not supported
# Bug

Output of `python -c "import pydantic.utils; print(pydantic.utils.version_info())"`:
```
             pydantic version: 1.4
            pydantic compiled: True
                 install path: /home/akear/.pyenv/versions/3.7.6/envs/dev/lib/python3.7/site-packages/pydantic
               python version: 3.7.6 (default, Jan  9 2020, 13:33:30)  [GCC 9.2.0]
                     platform: Linux-5.4.15-zen1-1-zen-x86_64-with-arch
     optional deps. installed: ['typing-extensions', 'devtools']

```

Hello ! Thank you for `pydantic`. I've found a bug when using `NewType`:

```py
import typing as t
import pydantic

Foo = t.NewType("Foo", t.List[int])

class Doc(pydantic.BaseModel):
    foo: Foo
```

Traceback:
```
Traceback (most recent call last):
  File "pydantic/validators.py", line 560, in pydantic.validators.find_validators
TypeError: issubclass() arg 1 must be a class

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "test.py", line 6, in <module>
    class Doc(pydantic.BaseModel):
  File "pydantic/main.py", line 199, in pydantic.main.ModelMetaclass.__new__
  File "pydantic/fields.py", line 277, in pydantic.fields.ModelField.infer
  File "pydantic/fields.py", line 249, in pydantic.fields.ModelField.__init__
  File "pydantic/fields.py", line 335, in pydantic.fields.ModelField.prepare
  File "pydantic/fields.py", line 468, in pydantic.fields.ModelField.populate_validators
  File "pydantic/validators.py", line 569, in find_validators
RuntimeError: error checking inheritance of typing.List[int] (type: List[int])
```

From what I understand, the current `NewType` handling done in `validators.py:find_validators` correctly resolve to `typing.List[int]` but doesn't know what to do with it since `typing.List[int]` is normally handled in `_type_analysis` and `find_validators` only sees the `int` type in this case.

Thanks !


Discussion:
Thanks for reporting, I'd say this was a feature request rather than a bug, though I agree it's a fine distinction.

I think at present `NewType` is not supported in this scenario, not sure how complex it would be to support without looking into it more.

If you're interested in having a try, I'd be happy to review a PR.


Resolving patch:
diff --git a/pydantic/fields.py b/pydantic/fields.py
--- a/pydantic/fields.py
+++ b/pydantic/fields.py
@@ -25,7 +25,16 @@
 from .error_wrappers import ErrorWrapper
 from .errors import NoneIsNotAllowedError
 from .types import Json, JsonWrapper
-from .typing import AnyType, Callable, ForwardRef, NoneType, display_as_type, is_literal_type
+from .typing import (
+    AnyType,
+    Callable,
+    ForwardRef,
+    NoneType,
+    display_as_type,
+    is_literal_type,
+    is_new_type,
+    new_type_supertype,
+)
 from .utils import PyObjectStr, Representation, lenient_issubclass, sequence_like
 from .validators import constant_validator, dict_validator, find_validators, validate_json
 
@@ -349,6 +358,8 @@ def _type_analysis(self) -> None:  # noqa: C901 (ignore complexity)
                 self.type_ = Union[self.type_.__constraints__]
             else:
                 self.type_ = Any
+        elif is_new_type(self.type_):
+            self.type_ = new_type_supertype(self.type_)
 
         if self.type_ is Any:
             if self.required is Undefined:
diff --git a/pydantic/validators.py b/pydantic/validators.py
--- a/pydantic/validators.py
+++ b/pydantic/validators.py
@@ -551,10 +551,6 @@ def find_validators(  # noqa: C901 (ignore complexity)
             yield any_class_validator
         return
 
-    supertype = _find_supertype(type_)
-    if supertype is not None:
-        type_ = supertype
-
     for val_type, validators in _VALIDATORS:
         try:
             if issubclass(type_, val_type):
@@ -572,18 +568,3 @@ def find_validators(  # noqa: C901 (ignore complexity)
         yield make_arbitrary_type_validator(type_)
     else:
         raise RuntimeError(f'no validator found for {type_}, see `arbitrary_types_allowed` in Config')
-
-
-def _find_supertype(type_: AnyType) -> Optional[AnyType]:
-    if not _is_new_type(type_):
-        return None
-
-    supertype = type_.__supertype__
-    if _is_new_type(supertype):
-        supertype = _find_supertype(supertype)
-
-    return supertype
-
-
-def _is_new_type(type_: AnyType) -> bool:
-    return hasattr(type_, '__name__') and hasattr(type_, '__supertype__')


Test patch:
diff --git a/tests/test_types.py b/tests/test_types.py
--- a/tests/test_types.py
+++ b/tests/test_types.py
@@ -1654,28 +1654,34 @@ def test_bounds_config_exceptions(fn):
 def test_new_type_success():
     a_type = NewType('a_type', int)
     b_type = NewType('b_type', a_type)
+    c_type = NewType('c_type', List[int])
 
     class Model(BaseModel):
         a: a_type
         b: b_type
+        c: c_type
 
-    m = Model(a=42, b=24)
-    assert m.dict() == {'a': 42, 'b': 24}
+    m = Model(a=42, b=24, c=[1, 2, 3])
+    assert m.dict() == {'a': 42, 'b': 24, 'c': [1, 2, 3]}
+    assert repr(Model.__fields__['c']) == "ModelField(name='c', type=List[int], required=True)"
 
 
 def test_new_type_fails():
     a_type = NewType('a_type', int)
     b_type = NewType('b_type', a_type)
+    c_type = NewType('c_type', List[int])
 
     class Model(BaseModel):
         a: a_type
         b: b_type
+        c: c_type
 
     with pytest.raises(ValidationError) as exc_info:
-        Model(a='foo', b='bar')
+        Model(a='foo', b='bar', c=['foo'])
     assert exc_info.value.errors() == [
         {'loc': ('a',), 'msg': 'value is not a valid integer', 'type': 'type_error.integer'},
         {'loc': ('b',), 'msg': 'value is not a valid integer', 'type': 'type_error.integer'},
+        {'loc': ('c', 0), 'msg': 'value is not a valid integer', 'type': 'type_error.integer'},
     ]