Repository: samuelcolvin/pydantic
Pull request #4263 (2022-07-21 15:26:56)

Add ISO 8601 wikipedia link to docs
<!-- Thank you for your contribution! -->
<!-- Unless your change is trivial, please create an issue to discuss the change before creating a PR -->
<!-- See https://pydantic-docs.helpmanual.io/contributing/ for help on Contributing -->

## Change Summary
Add ISO 8601 wikipedia link to docs
<!-- Please give a short summary of the changes. -->

## Related issue number

<!-- Are there any issues opened that will be resolved by merging this change? -->
<!-- WARNING: please use "fix #123" style references so the issue is closed when this PR is merged. -->

## Checklist

* [ ] Unit tests for the changes exist
* [ ] Tests pass on CI and coverage remains at 100%
* [*] Documentation reflects the changes where applicable
* [ ] `changes/<pull request or issue id>-<github username>.md` file added describing change
  (see [changes/README.md](https://github.com/samuelcolvin/pydantic/blob/master/changes/README.md) for details)
* [x] My PR is ready to review, **please add a comment including the phrase "please review" to assign reviewers**


hramezani: please review

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 240a25bce..d997845c1 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -103,7 +103,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        python-version: ['3.7', '3.8', '3.9', '3.10']
+        python-version: ['3.7', '3.8', '3.9', '3.10', '3.11.0-beta.5']
     env:
       PYTHON: ${{ matrix.python-version }}
       OS: ubuntu
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 2b0afc67c..31bc2b90d 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,12 +1,12 @@
-ansi2html==1.6.0
-flake8==4.0.1
+ansi2html==1.8.0
+flake8==5.0.3
 flake8-quotes==3.3.1
-hypothesis==6.48.2
-markdown-include==0.6.0
-mdx-truly-sane-lists==1.2
+hypothesis==6.53.0
+markdown-include==0.7.0
+mdx-truly-sane-lists==1.3
 mkdocs==1.3.1
 mkdocs-exclude==1.0.2
-mkdocs-material==8.3.8
+mkdocs-material==8.3.9
 sqlalchemy
 orjson
 ujson
diff --git a/docs/usage/exporting_models.md b/docs/usage/exporting_models.md
index 1673c45d5..62aca5b02 100644
--- a/docs/usage/exporting_models.md
+++ b/docs/usage/exporting_models.md
@@ -97,7 +97,7 @@ the values should be functions which serialise that type (see the example below)
 _(This script is complete, it should run "as is")_
 
 By default, `timedelta` is encoded as a simple float of total seconds. The `timedelta_isoformat` is provided
-as an optional alternative which implements ISO 8601 time diff encoding.
+as an optional alternative which implements [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) time diff encoding.
 
 The `json_encoders` are also merged during the models inheritance with the child
 encoders taking precedence over the parent one.
diff --git a/docs/usage/types.md b/docs/usage/types.md
index 0651bead5..02e9c43de 100644
--- a/docs/usage/types.md
+++ b/docs/usage/types.md
@@ -360,7 +360,7 @@ types:
   * `str`, following formats work:
 
     * `[-][DD ][HH:MM]SS[.ffffff]`
-    * `[±]P[DD]DT[HH]H[MM]M[SS]S` (ISO 8601 format for timedelta)
+    * `[±]P[DD]DT[HH]H[MM]M[SS]S` ([ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format for timedelta)
 
 ```py
 {!.tmp_examples/types_dt.py!}
diff --git a/pydantic/schema.py b/pydantic/schema.py
index d37e82206..4f29bd5c7 100644
--- a/pydantic/schema.py
+++ b/pydantic/schema.py
@@ -667,13 +667,11 @@ def enum_process_schema(enum: Type[Enum], *, field: Optional[ModelField] = None)
 
     This is similar to the `model_process_schema` function, but applies to ``Enum`` objects.
     """
-    from inspect import getdoc
-
     schema_: Dict[str, Any] = {
         'title': enum.__name__,
         # Python assigns all enums a default docstring value of 'An enumeration', so
         # all enums will have a description field even if not explicitly provided.
-        'description': getdoc(enum),
+        'description': enum.__doc__ or 'An enumeration.',
         # Add enum values and the enum field type to the schema.
         'enum': [item.value for item in cast(Iterable[Enum], enum)],
     }
diff --git a/pydantic/typing.py b/pydantic/typing.py
index 44fba3610..5a70a593a 100644
--- a/pydantic/typing.py
+++ b/pydantic/typing.py
@@ -132,6 +132,15 @@ else:
         """
         if hasattr(tp, '_nparams'):
             return (Any,) * tp._nparams
+        # Special case for `tuple[()]`, which used to return ((),) with `typing.Tuple`
+        # in python 3.10- but now returns () for `tuple` and `Tuple`.
+        # This will probably be clarified in pydantic v2
+        try:
+            if tp == Tuple[()] or sys.version_info >= (3, 9) and tp == tuple[()]:  # type: ignore[misc]
+                return ((),)
+        # there is a TypeError when compiled with cython
+        except TypeError:  # pragma: no cover
+            pass
         return ()
 
     def get_args(tp: Type[Any]) -> Tuple[Any, ...]:
diff --git a/pydantic/validators.py b/pydantic/validators.py
index d4783d97b..2c6565802 100644
--- a/pydantic/validators.py
+++ b/pydantic/validators.py
@@ -698,7 +698,7 @@ def find_validators(  # noqa: C901 (ignore complexity)
 
     class_ = get_class(type_)
     if class_ is not None:
-        if isinstance(class_, type):
+        if class_ is not Any and isinstance(class_, type):
             yield make_class_validator(class_)
         else:
             yield any_class_validator
diff --git a/requirements.txt b/requirements.txt
index 05e48dfcc..88fbe6955 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,7 +1,7 @@
 # requirements for compilation and from setup.py so dependabot prompts us to test with latest version of these packages
 
-Cython==0.29.30;sys_platform!='win32'
-devtools==0.8.0
+Cython==0.29.32;sys_platform!='win32'
+devtools==0.9.0
 email-validator==1.2.1
 dataclasses==0.6; python_version < '3.7'
 typing-extensions==4.3.0
diff --git a/setup.cfg b/setup.cfg
index ed77c38ea..a3fb54b9e 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -8,6 +8,9 @@ filterwarnings =
     # for Python 3.10+: mypy still relies on distutils on windows. We hence ignore those warnings
     ignore:The distutils package is deprecated and slated for removal in Python 3.12:DeprecationWarning
     ignore:The distutils.sysconfig module is deprecated, use sysconfig instead:DeprecationWarning
+    # for Python 3.11
+    ignore:path is deprecated.*:DeprecationWarning:certifi
+    ignore:module 'sre_constants' is deprecated:DeprecationWarning:pkg_resources
 
 [flake8]
 max-line-length = 120


Test patch:
diff --git a/tests/requirements-linting.txt b/tests/requirements-linting.txt
index b8e2452c8..50de9c43c 100644
--- a/tests/requirements-linting.txt
+++ b/tests/requirements-linting.txt
@@ -1,11 +1,11 @@
 black==22.6.0
-flake8==4.0.1
+flake8==5.0.3
 flake8-quotes==3.3.1
-hypothesis==6.48.2
+hypothesis==6.53.0
 isort==5.10.1
-pyupgrade==2.34.0
+pyupgrade==2.37.3
 mypy==0.950
-pre-commit==2.19.0
-pycodestyle==2.8.0
-pyflakes==2.4.0
+pre-commit==2.20.0
+pycodestyle==2.9.0
+pyflakes==2.5.0
 twine==4.0.1
diff --git a/tests/requirements-testing.txt b/tests/requirements-testing.txt
index 09b8b13a8..0f8ac5d6f 100644
--- a/tests/requirements-testing.txt
+++ b/tests/requirements-testing.txt
@@ -1,9 +1,9 @@
-coverage==6.4.1
-hypothesis==6.48.2
+coverage==6.4.2
+hypothesis==6.53.0
 # pin importlib-metadata as upper versions need typing-extensions to work if on Python < 3.8
 importlib-metadata==3.1.0;python_version<"3.8"
 mypy==0.950
 pytest==7.1.2
 pytest-cov==3.0.0
-pytest-mock==3.8.1
-pytest-sugar==0.9.4
+pytest-mock==3.8.2
+pytest-sugar==0.9.5
diff --git a/tests/test_edge_cases.py b/tests/test_edge_cases.py
index b7083d058..4b6c056c1 100644
--- a/tests/test_edge_cases.py
+++ b/tests/test_edge_cases.py
@@ -14,6 +14,7 @@ from pydantic import (
     NoneStrBytes,
     StrBytes,
     ValidationError,
+    compiled,
     constr,
     errors,
     validate_model,
@@ -236,6 +237,97 @@ def test_tuple_more():
     }
 
 
+@pytest.mark.parametrize(
+    'dict_cls,frozenset_cls,list_cls,set_cls,tuple_cls,type_cls',
+    [
+        (Dict, FrozenSet, List, Set, Tuple, Type),
+        (dict, frozenset, list, set, tuple, type),
+    ],
+)
+@pytest.mark.skipif(
+    sys.version_info < (3, 9) or compiled, reason='PEP585 generics only supported for python 3.9 and above'
+)
+def test_pep585_generic_types(dict_cls, frozenset_cls, list_cls, set_cls, tuple_cls, type_cls):
+    class Type1:
+        pass
+
+    class Type2:
+        pass
+
+    class Model(BaseModel, arbitrary_types_allowed=True):
+        a: dict_cls
+        a1: dict_cls[str, int]
+        b: frozenset_cls
+        b1: frozenset_cls[int]
+        c: list_cls
+        c1: list_cls[int]
+        d: set_cls
+        d1: set_cls[int]
+        e: tuple_cls
+        e1: tuple_cls[int]
+        e2: tuple_cls[int, ...]
+        e3: tuple_cls[()]
+        f: type_cls
+        f1: type_cls[Type1]
+
+    default_model_kwargs = dict(
+        a={},
+        a1={'a': '1'},
+        b=[],
+        b1=('1',),
+        c=[],
+        c1=('1',),
+        d=[],
+        d1=['1'],
+        e=[],
+        e1=['1'],
+        e2=['1', '2'],
+        e3=[],
+        f=Type1,
+        f1=Type1,
+    )
+
+    m = Model(**default_model_kwargs)
+    assert m.a == {}
+    assert m.a1 == {'a': 1}
+    assert m.b == frozenset()
+    assert m.b1 == frozenset({1})
+    assert m.c == []
+    assert m.c1 == [1]
+    assert m.d == set()
+    assert m.d1 == {1}
+    assert m.e == ()
+    assert m.e1 == (1,)
+    assert m.e2 == (1, 2)
+    assert m.e3 == ()
+    assert m.f == Type1
+    assert m.f1 == Type1
+
+    with pytest.raises(ValidationError) as exc_info:
+        Model(**(default_model_kwargs | {'e3': (1,)}))
+    assert exc_info.value.errors() == [
+        {
+            'ctx': {'actual_length': 1, 'expected_length': 0},
+            'loc': ('e3',),
+            'msg': 'wrong tuple length 1, expected 0',
+            'type': 'value_error.tuple.length',
+        }
+    ]
+
+    Model(**(default_model_kwargs | {'f': Type2}))
+
+    with pytest.raises(ValidationError) as exc_info:
+        Model(**(default_model_kwargs | {'f1': Type2}))
+    assert exc_info.value.errors() == [
+        {
+            'ctx': {'expected_class': 'Type1'},
+            'loc': ('f1',),
+            'msg': 'subclass of Type1 expected',
+            'type': 'type_error.subclass',
+        }
+    ]
+
+
 def test_tuple_length_error():
     class Model(BaseModel):
         v: Tuple[int, float, bool]
diff --git a/tests/test_fastapi.sh b/tests/test_fastapi.sh
index ff5a3e3ad..26fcc2174 100755
--- a/tests/test_fastapi.sh
+++ b/tests/test_fastapi.sh
@@ -9,7 +9,6 @@ latest_tag_commit=$(git rev-list --tags --max-count=1)
 latest_tag=$(git describe --tags "${latest_tag_commit}")
 git checkout "${latest_tag}"
 
-pip install -U flit
-flit install
+pip install .[all,dev,doc,test]
 
 ./scripts/test.sh