Skip to content

Unable to annotate any partialmethod used in SerializerMethodField #451

@sevdog

Description

@sevdog

Describe the bug
When a serializer defines any SerializerMethodField as a functools.partialmethod is impossible to specify any extra attribute on this method, thus resulting in a typerror.

Traceback (most recent call last):       
  File "/usr/local/bin/django-admin", line 8, in <module>                                                                                                                                                          
    sys.exit(execute_from_command_line())           
  File "/usr/local/lib/python3.8/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
    utility.execute()                                                                                    
  File "/usr/local/lib/python3.8/site-packages/django/core/management/__init__.py", line 395, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)                                                                                                                                                        
  File "/usr/local/lib/python3.8/site-packages/django/core/management/base.py", line 330, in run_from_argv
    self.execute(*args, **cmd_options)                                                                   
  File "/usr/local/lib/python3.8/site-packages/django/core/management/base.py", line 371, in execute
    output = self.handle(*args, **options)                                                               
  File "/usr/local/lib/python3.8/site-packages/drf_spectacular/management/commands/spectacular.py", line 50, in handle
    schema = generator.get_schema(request=None, public=True)                                                                                                                                                       
  File "/usr/local/lib/python3.8/site-packages/drf_spectacular/generators.py", line 257, in get_schema
    paths=self.parse(request, public),                                                                   
  File "/usr/local/lib/python3.8/site-packages/drf_spectacular/generators.py", line 231, in parse                                                                                                                  
    operation = view.schema.get_operation(                                                                                                                                                                         
  File "/usr/local/lib/python3.8/site-packages/drf_spectacular/openapi.py", line 84, in get_operation
    operation['responses'] = self._get_response_bodies()            
  File "/usr/local/lib/python3.8/site-packages/drf_spectacular/openapi.py", line 1013, in _get_response_bodies
    return {'200': self._get_response_for_code(response_serializers, '200')}                             
  File "/usr/local/lib/python3.8/site-packages/drf_spectacular/openapi.py", line 1058, in _get_response_for_code
    component = self.resolve_serializer(serializer, 'response')            
  File "/usr/local/lib/python3.8/site-packages/drf_spectacular/openapi.py", line 1204, in resolve_serializer
    component.schema = self._map_serializer(serializer, direction)                 
  File "/usr/local/lib/python3.8/site-packages/drf_spectacular/openapi.py", line 705, in _map_serializer
    schema = self._map_basic_serializer(serializer, direction)             
  File "/usr/local/lib/python3.8/site-packages/drf_spectacular/openapi.py", line 769, in _map_basic_serializer
    schema = self._map_serializer_field(field, direction)                          
  File "/usr/local/lib/python3.8/site-packages/drf_spectacular/openapi.py", line 646, in _map_serializer_field
    return append_meta(self._map_response_type_hint(method), meta)                                                                                                                                                 
  File "/usr/local/lib/python3.8/site-packages/drf_spectacular/openapi.py", line 831, in _map_response_type_hint
    hint = get_override(method, 'field') or typing.get_type_hints(method).get('return')                  
  File "/usr/local/lib/python3.8/typing.py", line 1255, in get_type_hints
    raise TypeError('{!r} is not a module, class, method, '
TypeError: functools.partial(<bound method MySerializer._get_generic_ct_field of MySerializer():
    element = SerializerMethodField()       
    other= SerializerMethodField() >, field='other') is not a module, class, method, or function.

To Reproduce

Just define a basic serializer with a partialmethod:

from functools import partialmethod
from rest_framework import serializers
from drf_spectacular.utils import extend_schema_field
from drf_spectacular.types import OpenApiTypes


class MySerializer(serializers.Serializer):
    foo = serializers.SerializerMethodField()
    bar = serializers.SerializerMethodField()

    @extend_schema_field(OpenApiTypes.ANY)
    def _private_method(self, field, extra_param):
        # do some real stuff
        print(field, extra_param)

    def _private_method_2(self, field, extra_param):
        # do some real stuff
        print(field, extra_param)
    
    get_foo = partialmethod(_private_method, extra_param='foo')
    get_bar = extend_schema_field(OpenApiTypes.ANY)(partialmethod(_private_method_2, extra_param='bar'))

This will throw the abovementioned error.

Expected behavior
No error should be thrown, if type annotation was setted on base method it should be used.

NOTE: as I have tested using the decorator inline is useless, since it is not possible to add any attrbute on partialmethod objects.

As of now a partialmethod object can be recognized since it has an attribute called _partialmethod where it stores the original partial object which is called by the interpreter.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestfix confirmation pendingissue has been fixed and confirmation from issue reporter is pending

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions