Search code examples
pythondjangodjango-rest-framework

Advice on using patch file in django for installed library


I am using rest_framework_simple_api_key in my production application on python version 3.9 . On running command

python generate_fernet_keymanage.py as given in doc(djangorestframework-simple-apikey) i am getting

File "C:\Users\DELL\anaconda3\lib\site-packages\rest_framework_simple_api_key\models.py", line 15, in <module>
class AbstractAPIKeyManager(models.Manager):
File "C:\Users\DELL\anaconda3\lib\site-packages\rest_framework_simple_api_key\models.py", line 16, in AbstractAPIKeyManager
def get_api_key(self, pk: int | str):
TypeError: unsupported operand type(s) for |: 'type' and 'type'

On Searching I got reason is i am getting error is The error TypeError: unsupported operand type(s) for |: 'type' and 'type' is caused by the use of the int | str syntax for type hinting, which is only supported in Python 3.10 and later versions. I can't change my python version as it is in production so I came across solution monkey patching then i got this article https://medium.com/lemon-code/monkey-patch-f1de778d61d3 my monkey_patch.py file:

def patch_get_api_key():
    print("*********************************EXE****************************************")
    """
    Monkey patch for AbstractAPIKeyManager.get_api_key method to replace the type hint.
    """
    from typing import Union
    def patched_get_api_key(self, pk: Union[int, str]):
        try:
            print("Patched get_api_key method")
            return self.get(pk=pk)
        except self.model.DoesNotExist:
            return None
    print("Before import")
    import rest_framework_simple_api_key.models as models
    print("After import")    
models.AbstractAPIKeyManager.get_api_key = patched_get_api_key

I added code in my apps.py file:

# myapp/apps.py

from django.apps import AppConfig

class MyCustomAppConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'roomroot'

    def ready(self):
        """ Load monkey patching. """
        try:
            from .monkey_patch import patch_get_api_key
            patch_get_api_key()
        except ImportError:
            pass

and called it in manage.py file:

def main():
    """Run administrative tasks."""
    
    settings_module = "roomroot.deployment" if "WEBSITEHOSTNAME" in os.environ else  "roomroot.settings"

    os.environ.setdefault("DJANGO_SETTINGS_MODULE", settings_module)
    from roomroot.monkey_patch import patch_get_api_key
    patch_get_api_key()

by running command for generating generate_fernet_key i am getting error:

python manage.py generate_fernet_key
*********************************EXE****************************************
Before import
Traceback (most recent call last):
File "F:\Abha\Room_Reveal\Backend\roomroot\manage.py", line 27, in <module>
main()
File "F:\Abha\Room_Reveal\Backend\roomroot\manage.py", line 14, in main
patch_get_api_key()
File "F:\Abha\Room_Reveal\Backend\roomroot\roomroot\monkey_patch.py", line 18, in patch_get_api_key
from rest_framework_simple_api_key.models import AbstractAPIKeyManager
File "C:\Users\DELL\anaconda3\lib\site-packages\rest_framework_simple_api_key\models.py", line 15, in <module>
class AbstractAPIKeyManager(models.Manager):
File "C:\Users\DELL\anaconda3\lib\site-packages\rest_framework_simple_api_key\models.py", line 16, in AbstractAPIKeyManager
def get_api_key(self, pk: int | str):
TypeError: unsupported operand type(s) for |: 'type' and 'type'

My question is using patch to do resolve this error is good idea? Also I tried calling my patch_get_api_key() in setting.py file still getting type error.


Solution

  • No amount of monkey patching is going to help you solve that issue. To monkey patch the method you need to import the module, when the module is being imported / loaded the class definition and hence the method definition would occur causing the TypeError before you ever have the chance to patch it.

    You have two sensible solutions:

    1. Upgrade to Python 3.10
    2. Fork the package causing the error and make it compatible with Python 3.9 (the version you are using), or request the package maintainer to make it compatible.