Skip to content

SerializerMethodField return type is always 'STRING' when a file uses from __future__ import annotations due to PEP 563 #866

Open
@derlin

Description

@derlin

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' return String.BOOLEAN (same for other types)
  • or use the new methods if available, and clearly state the issue for older python versions in the docs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    triageNo assigned version

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions