Skip to content
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

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

Open
derlin opened this issue Sep 1, 2023 · 2 comments

Comments

@derlin
Copy link

derlin commented Sep 1, 2023

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.
@derlin derlin changed the title SerializerMethodField return type is always 'STRING' when a file imports from __future__ import annotations due to PEP 563 SerializerMethodField return type is always 'STRING' when a file uses from __future__ import annotations due to PEP 563 Sep 1, 2023
@derlin
Copy link
Author

derlin commented Sep 13, 2023

See this blog post for more information Python, type hints, and future annotations

@derlin
Copy link
Author

derlin commented Dec 20, 2023

ping?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant