-
-
Notifications
You must be signed in to change notification settings - Fork 8.6k
🐛 Fix inconsistent processing of model docstring formfeed char with Pydantic V1 #6039
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
🐛 Fix inconsistent processing of model docstring formfeed char with Pydantic V1 #6039
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
8a90482 to
5d3b953
Compare
This comment was marked as outdated.
This comment was marked as outdated.
0eef5f7 to
a4ea039
Compare
|
📝 Docs preview for commit 3a15194 at: https://63f7bcb8cac94d008f451d7c--fastapi.netlify.app |
…ription-formfeed-inconsistency
YuriiMotov
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Preconditions:
- Pydantic V1
- Nested model has description with formfeed character
Symptoms:
Generated openapi schema will be inconsistent (sometimes the part after \f will be truncated, sometimes not).
test_openapi_schema (from test_get_model_definitions_formfeed_escape.py) will fail sometimes.
test_model_description_escaped_with_formfeed will always fail for sort_reversed=False.
Tests in test_get_model_definitions_formfeed_escape.py prove that the result of _compat.get_model_definitions depends on the order of models passed in flat_models parameter.
Explanation:
Addressis nested intoFacility.- When
Addressmodel goes beforeFacilityinflat_models(input parameter of_compat.get_model_definitions), it's added todefinitionsand itsdescriptionis updated (truncated by formfeed charecter). - But on the second iteration of for-loop, we have
Addressinm_definitions(sinceAddressis nested intoFacility), anddefinitions.update(m_definitions)overrides existing schema ofAddressindefinitions. - So,
definitionsnow contain theAddressschema with originaldescription(not truncated).
The idea of the fix is to add all models to definitions first and only then update description's of all models.
Looks good to me.
tiangolo
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, sounds good, thank you! 🚀
| class SortedTypeSet(set): | ||
| """ | ||
| Set of Types whose `__iter__()` method yields results sorted by the type names | ||
| """ | ||
|
|
||
| def __init__(self, seq: Set[Type[Any]], *, sort_reversed: bool): | ||
| super().__init__(seq) | ||
| self.sort_reversed = sort_reversed | ||
|
|
||
| def __iter__(self) -> Iterator[Type[Any]]: | ||
| members_sorted = sorted( | ||
| super().__iter__(), | ||
| key=lambda type_: type_.__name__, | ||
| reverse=self.sort_reversed, | ||
| ) | ||
| yield from members_sorted |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very fancy! ✨ ✔️
Problem
Generating openapi specs for models with a formfeed (
\f) in the docstring yields inconsistent results. For example, in the case of:Sometimes the openapi
descriptionofAddresscorrectly renders asThis is a public description of an Address\n. But other times, you get the entire docstring even though the\fshould be keeping the remainder of the docstring private.The bug seemed to be in the following function:
The issue is that the
definitionsdict has its entries updated in two different places:definitions.update(m_definitions)definitions[model_name] = m_schemaWhen a model definition is written in line (2), the proper formfeed escape of the docstring is persisted. But if the same model is subsequently written by line (1), the previous docstring fix is clobbered over and we see the entire docstring.
This is a non-deterministic error, since
flat_modelsis an unordered set. Depending on order of models in iteration, you could end up with either (1) or (2) as the final edit to a given model.Solution
This fix works around the bug by handling the formfeed escape at the end of model processing. By that point our
definitionsdict will be final, so any edits to the models are guaranteed to make it into the return value.Related issues
Questions & Alternatives
definitionsis updated in two different ways?