Description
Bug Report
Description
PEP 563 – Postponed Evaluation of Annotations is coming along, and breaks the inference of return types in SerializerMethodField
. In short:
This PEP proposes changing function annotations and variable annotations so that they are no longer evaluated at function definition time. Instead, they are preserved in
__annotations__
in string form.This change is being introduced gradually, starting with a future import in Python 3.7.
It isn't yet activated by default, but gets activated on any file importing:
from __future__ import annotations
When this feature is on, https://github.com/axnsan12/drf-yasg/blob/1.21.7/src/drf_yasg/inspectors/field.py#L616 fails to guess the return type, since it assumes the type returned is actually a Type
, but it is now a str
(see example below).
This will be completely broken once PEP 563 becomes the default.
Is this a regression?
Well, yes and no. It is a regression in the sense that the from __future__ import annotations
is more and more often used, as it solves some problems such as forward references and cyclic imports.
Minimal Reproduction
Here is an example of the problem:
from __future__ import annotations
from rest_framework import serializers
class MySerializer(serializers.Serializer):
# This allows to return something that is not a direct
# attribute of a model, but a "computed" value
is_foo = serializers.SerializerMethodField()
# The type hint is important for drf-yasg to
# guess the proper type
def get_is_foo(self, obj) -> bool:
return obj.is_foo
In the above example, drf-yasg will wrongly assume {"is_foo": "STRING"}
instead of {"is_foo": "BOOLEAN"}
, because inspect.get_signature(is_foo)
returns 'bool'
(str) and not bool
(type).
Possible fixes
In order to un-stringize the type, Python 3.10 introduced a new method, inspect.get_annotations(f, eval_str=True)
and ported the eval_str
parameter to inspect.signature
. For Python 3.9 and older, this needs to be done manually.
The possible fixes are thus:
- either change the logic so that both
bool
and'bool'
returnString.BOOLEAN
(same for other types) - or use the new methods if available, and clearly state the issue for older python versions in the docs.