Repository: pydantic/pydantic
Pull request: Change schema frozenset
<!-- 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 one type(frozenset) to schema.py/field_class_to_schema

<!-- Please give a short summary of the changes. -->

## Related issue number

fix #1557

<!-- Are there any issues opened that will be resolved by merging this change? -->

## 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)


Problem statement:
add frozenset to schema.py/field_class_to_schema
# Feature Request

             pydantic version: 1.5.1
            pydantic compiled: False
                 install path: /home/wang/PycharmProjects/FastApiLearn/venv/lib/python3.6/site-packages/pydantic
               python version: 3.6.5 (default, May 11 2018, 13:30:17)  [GCC 7.3.0]
                     platform: Linux-4.15.0-29deepin-generic-x86_64-with-Deepin-15.11-stable
     optional deps. installed: []

```
Use pydantic to validate frozenset in FastApi, when I open SwaggerUI in Chrome, error occurs.
Add frozenset type in schema.py/field_class_to_schema to resolve the error.

```exception
Traceback (most recent call last):
  File "/home/wang/PycharmProjects/FastApiLearn/venv/lib/python3.6/site-packages/uvicorn/protocols/http/httptools_impl.py", line 385, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/home/wang/PycharmProjects/FastApiLearn/venv/lib/python3.6/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "/home/wang/PycharmProjects/FastApiLearn/venv/lib/python3.6/site-packages/fastapi/applications.py", line 148, in __call__
    await super().__call__(scope, receive, send)  # pragma: no cover
  File "/home/wang/PycharmProjects/FastApiLearn/venv/lib/python3.6/site-packages/starlette/applications.py", line 102, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/wang/PycharmProjects/FastApiLearn/venv/lib/python3.6/site-packages/starlette/middleware/errors.py", line 181, in __call__
    raise exc from None
  File "/home/wang/PycharmProjects/FastApiLearn/venv/lib/python3.6/site-packages/starlette/middleware/errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "/home/wang/PycharmProjects/FastApiLearn/venv/lib/python3.6/site-packages/starlette/exceptions.py", line 82, in __call__
    raise exc from None
  File "/home/wang/PycharmProjects/FastApiLearn/venv/lib/python3.6/site-packages/starlette/exceptions.py", line 71, in __call__
    await self.app(scope, receive, sender)
  File "/home/wang/PycharmProjects/FastApiLearn/venv/lib/python3.6/site-packages/starlette/routing.py", line 550, in __call__
    await route.handle(scope, receive, send)
  File "/home/wang/PycharmProjects/FastApiLearn/venv/lib/python3.6/site-packages/starlette/routing.py", line 227, in handle
    await self.app(scope, receive, send)
  File "/home/wang/PycharmProjects/FastApiLearn/venv/lib/python3.6/site-packages/starlette/routing.py", line 41, in app
    response = await func(request)
  File "/home/wang/PycharmProjects/FastApiLearn/venv/lib/python3.6/site-packages/fastapi/applications.py", line 103, in openapi
    return JSONResponse(self.openapi())
  File "/home/wang/PycharmProjects/FastApiLearn/venv/lib/python3.6/site-packages/fastapi/applications.py", line 95, in openapi
    openapi_prefix=self.openapi_prefix,
  File "/home/wang/PycharmProjects/FastApiLearn/venv/lib/python3.6/site-packages/fastapi/openapi/utils.py", line 291, in get_openapi
    flat_models=flat_models, model_name_map=model_name_map
  File "/home/wang/PycharmProjects/FastApiLearn/venv/lib/python3.6/site-packages/fastapi/utils.py", line 84, in get_model_definitions
    model, model_name_map=model_name_map, ref_prefix=REF_PREFIX
  File "/home/wang/PycharmProjects/FastApiLearn/venv/lib/python3.6/site-packages/pydantic/schema.py", line 456, in model_process_schema
    model, by_alias=by_alias, model_name_map=model_name_map, ref_prefix=ref_prefix, known_models=known_models
  File "/home/wang/PycharmProjects/FastApiLearn/venv/lib/python3.6/site-packages/pydantic/schema.py", line 492, in model_type_schema
    f, by_alias=by_alias, model_name_map=model_name_map, ref_prefix=ref_prefix, known_models=known_models
  File "/home/wang/PycharmProjects/FastApiLearn/venv/lib/python3.6/site-packages/pydantic/schema.py", line 191, in field_schema
    known_models=known_models or set(),
  File "/home/wang/PycharmProjects/FastApiLearn/venv/lib/python3.6/site-packages/pydantic/schema.py", line 418, in field_type_schema
    known_models=known_models,
  File "/home/wang/PycharmProjects/FastApiLearn/venv/lib/python3.6/site-packages/pydantic/schema.py", line 686, in field_singleton_schema
    raise ValueError(f'Value not declarable with JSON Schema, field: {field}')
ValueError: Value not declarable with JSON Schema, field: name='a' type=frozenset required=True


```schema.py 

field_class_to_schema: Tuple[Tuple[Any, Dict[str, Any]], ...] = (
    (Path, {'type': 'string', 'format': 'path'}),
    (datetime, {'type': 'string', 'format': 'date-time'}),
    (date, {'type': 'string', 'format': 'date'}),
    (time, {'type': 'string', 'format': 'time'}),
    (timedelta, {'type': 'number', 'format': 'time-delta'}),
    (IPv4Network, {'type': 'string', 'format': 'ipv4network'}),
    (IPv6Network, {'type': 'string', 'format': 'ipv6network'}),
    (IPv4Interface, {'type': 'string', 'format': 'ipv4interface'}),
    (IPv6Interface, {'type': 'string', 'format': 'ipv6interface'}),
    (IPv4Address, {'type': 'string', 'format': 'ipv4'}),
    (IPv6Address, {'type': 'string', 'format': 'ipv6'}),
    (str, {'type': 'string'}),
    (bytes, {'type': 'string', 'format': 'binary'}),
    (bool, {'type': 'boolean'}),
    (int, {'type': 'integer'}),
    (float, {'type': 'number'}),
    (Decimal, {'type': 'number'}),
    (UUID, {'type': 'string', 'format': 'uuid'}),
    (dict, {'type': 'object'}),
    (list, {'type': 'array', 'items': {}}),
    (tuple, {'type': 'array', 'items': {}}),
    (set, {'type': 'array', 'items': {}, 'uniqueItems': True}),
    (frozenset, {'type': 'array', 'items': {}, 'uniqueItems': True}),  # my addition
)
```

Hints from issue discussion:
Could you fix your formatting in the issue?

It would be useful to see a demonstration of the problem: e.g. code that raises an exception.
I have added my exception.
I meant a self contained example that demonstrates the problem.

E.g.:

```py
from pydantic import BaseModel

...
```
```
from pydantic import BaseModel
class Item(BaseModel):
    item1: frozenset
```
This piece of code is ok. But when I read "SwaggerUI" in web framework "FastApi", the above exception occured.
That's not a demonstration of the problem. The error can be caused with:

```py
from pydantic import BaseModel


class Item(BaseModel):
    item1: frozenset


item = Item(item1={1, 2, 3})
print(item.schema())
```
exception:
```
Traceback (most recent call last):
  File "test.py", line 9, in <module>
    print(item.schema())
  File "/Users/samuel/code/pydantic/pydantic/main.py", line 558, in schema
    s = model_schema(cls, by_alias=by_alias)
  File "/Users/samuel/code/pydantic/pydantic/schema.py", line 137, in model_schema
    m_schema, m_definitions, nested_models = model_process_schema(
  File "/Users/samuel/code/pydantic/pydantic/schema.py", line 465, in model_process_schema
    m_schema, m_definitions, nested_models = model_type_schema(
  File "/Users/samuel/code/pydantic/pydantic/schema.py", line 501, in model_type_schema
    f_schema, f_definitions, f_nested_models = field_schema(
  File "/Users/samuel/code/pydantic/pydantic/schema.py", line 190, in field_schema
    f_schema, f_definitions, f_nested_models = field_type_schema(
  File "/Users/samuel/code/pydantic/pydantic/schema.py", line 417, in field_type_schema
    f_schema, f_definitions, f_nested_models = field_singleton_schema(
  File "/Users/samuel/code/pydantic/pydantic/schema.py", line 733, in field_singleton_schema
    raise ValueError(f'Value not declarable with JSON Schema, field: {field}')
ValueError: Value not declarable with JSON Schema, field: name='item1' type=frozenset required=True
```
This is what I was looking for.

PR welcome to to fix this.

Commit message: add my test

Patch to review:
diff --git a/changes/1557-wangpeibao.md b/changes/1557-wangpeibao.md
new file mode 100644
index 00000000000..93eae4c0d11
--- /dev/null
+++ b/changes/1557-wangpeibao.md
@@ -0,0 +1,2 @@
+change schema.py/field_class_to_schema
+add frozenset type to the tuple
diff --git a/changes/index-1.html b/changes/index-1.html
new file mode 100644
index 00000000000..302384d261d
--- /dev/null
+++ b/changes/index-1.html
@@ -0,0 +1,3 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><title>未知 </title></head><body>
+<p>Fix model validation to handle nested literals, e.g. <code>Literal['foo', Literal['bar']]</code>.</p>
+</body></html>
\ No newline at end of file
diff --git a/pydantic/schema.py b/pydantic/schema.py
index 3ca516a9063..9562e74b46d 100644
--- a/pydantic/schema.py
+++ b/pydantic/schema.py
@@ -623,6 +623,7 @@ def field_singleton_sub_fields_schema(
     (list, {'type': 'array', 'items': {}}),
     (tuple, {'type': 'array', 'items': {}}),
     (set, {'type': 'array', 'items': {}, 'uniqueItems': True}),
+    (frozenset, {'type': 'array', 'items': {}, 'uniqueItems': True}),
 )
 
 json_scheme = {'type': 'string', 'format': 'json-string'}
diff --git a/tests/test_schema.py b/tests/test_schema.py
index ce447d5ec73..a068fc8000a 100644
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -1837,7 +1837,10 @@ class Model(BaseModel):
 def test_frozen_set():
     class Model(BaseModel):
         a: FrozenSet[int] = frozenset({1, 2, 3})
+        b: FrozenSet = frozenset({1, 2, 3})
+        c: frozenset = frozenset({1, 2, 3})
 
+    print(Model.schema())
     assert Model.schema() == {
         'title': 'Model',
         'type': 'object',
@@ -1848,7 +1851,9 @@ class Model(BaseModel):
                 'type': 'array',
                 'items': {'type': 'integer'},
                 'uniqueItems': True,
-            }
+            },
+            'b': {'title': 'B', 'default': frozenset({1, 2, 3}), 'type': 'array', 'items': {}, 'uniqueItems': True},
+            'c': {'title': 'C', 'default': frozenset({1, 2, 3}), 'type': 'array', 'items': {}, 'uniqueItems': True},
         },
     }
 


Code review comments:
diff_hunk: @@ -0,0 +1,3 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><title>未知 </title></head><body>
path: changes/index-1.html
text: @user1:
this file should be removed.