Repository: pydantic/pydantic

Issue:
Subclassing StrictStr / ConstrainedStr causes ValidationError
# Bug

Please complete:
* OS: **Manjaro Linux**
* Python version: **3.7.4**
* Pydantic version: **1.2.0**

------------------------------------------------------------------
### Original bug discussion in #799 
------------------------------------------------------------------

I am sorta lost as to why this isn't working as expected for me in v1.2:

```py
from typing import Type, TypeVar
import pydantic


class ConnectionName(pydantic.StrictStr):
    min_length = 1


class EndpointStr(pydantic.StrictStr):
    min_length = 1


DEFAULT_ENDPOINT = EndpointStr("default")
T = TypeVar("T", bound="UpRequestEvent")


class UpRequestEvent(pydantic.BaseModel):
    connection_name: ConnectionName  # connection name
    connection_endpoint: EndpointStr  # connection name's endpoint (for server type connections)

    @classmethod
    def build_from(
        cls: Type[T],
        connection_name: ConnectionName,
        connection_endpoint: EndpointStr = DEFAULT_ENDPOINT,
        **kwargs
    ) -> T:
        return cls(
            connection_name=connection_name,
            connection_endpoint=connection_endpoint,
        )

    class Config:
        use_enum_values = True


# This works
UpRequestEvent.build_from(
    connection_name="test", connection_endpoint="default",
)

# This fails
UpRequestEvent.build_from(
    connection_name=ConnectionName("test"), connection_endpoint=EndpointStr("default"),
)
```

```
Traceback (most recent call last):
  File "/opt/pycharm-eap/plugins/python/helpers/pydev/pydevd.py", line 1415, in _exec
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "/opt/pycharm-eap/plugins/python/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "/home/spalmer1009/.PyCharm2019.3/config/scratches/scratch.py", line 44, in <module>
    connection_name=ConnectionName("test"), connection_endpoint=EndpointStr("default"),
  File "/home/spalmer1009/.PyCharm2019.3/config/scratches/scratch.py", line 30, in build_from
    connection_endpoint=connection_endpoint,
  File "pydantic/main.py", line 274, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 2 validation errors for UpRequestEvent
connection_name
  Expected unicode, got ConnectionName (type=type_error)
connection_endpoint
  Expected unicode, got EndpointStr (type=type_error)
```

My special types ConnectionName and EndpointStr are a subclass of `str` (through `StrictStr` -> `ConstrainedStr`)

---------------------------------------

As discovered by @dmontagu the issue only seems to exist when the code is Cython compiled.




Discussion:

Resolving patch:
diff --git a/pydantic/types.py b/pydantic/types.py
--- a/pydantic/types.py
+++ b/pydantic/types.py
@@ -157,7 +157,7 @@ def __get_validators__(cls) -> 'CallableGenerator':
         yield cls.validate
 
     @classmethod
-    def validate(cls, value: str) -> str:
+    def validate(cls, value: Union[str]) -> Union[str]:
         if cls.curtail_length and len(value) > cls.curtail_length:
             value = value[: cls.curtail_length]
 
diff --git a/pydantic/validators.py b/pydantic/validators.py
--- a/pydantic/validators.py
+++ b/pydantic/validators.py
@@ -55,7 +55,7 @@ def str_validator(v: Any) -> Optional[str]:
         raise errors.StrError()
 
 
-def strict_str_validator(v: Any) -> str:
+def strict_str_validator(v: Any) -> Union[str]:
     if isinstance(v, str):
         return v
     raise errors.StrError()


Test patch:
diff --git a/tests/test_types.py b/tests/test_types.py
--- a/tests/test_types.py
+++ b/tests/test_types.py
@@ -948,6 +948,18 @@ class Model(BaseModel):
         Model(v=b'foobar')
 
 
+def test_strict_str_subclass():
+    class MyStrictStr(StrictStr):
+        pass
+
+    class Model(BaseModel):
+        v: MyStrictStr
+
+    m = Model(v=MyStrictStr('foobar'))
+    assert isinstance(m.v, MyStrictStr)
+    assert m.v == 'foobar'
+
+
 def test_strict_bool():
     class Model(BaseModel):
         v: StrictBool
@@ -978,6 +990,18 @@ class Model(BaseModel):
         Model(v=3.14159)
 
 
+def test_strict_int_subclass():
+    class MyStrictInt(StrictInt):
+        pass
+
+    class Model(BaseModel):
+        v: MyStrictInt
+
+    m = Model(v=MyStrictInt(123456))
+    assert isinstance(m.v, MyStrictInt)
+    assert m.v == 123456
+
+
 def test_strict_float():
     class Model(BaseModel):
         v: StrictFloat
@@ -991,6 +1015,18 @@ class Model(BaseModel):
         Model(v=123456)
 
 
+def test_strict_float_subclass():
+    class MyStrictFloat(StrictFloat):
+        pass
+
+    class Model(BaseModel):
+        v: MyStrictFloat
+
+    m = Model(v=MyStrictFloat(3.14159))
+    assert isinstance(m.v, MyStrictFloat)
+    assert m.v == 3.14159
+
+
 def test_bool_unhashable_fails():
     class Model(BaseModel):
         v: bool