Quantcast
Channel: Active questions tagged python - Stack Overflow
Viewing all articles
Browse latest Browse all 14155

decorator declared and used inside of class [duplicate]

$
0
0

I wrote a manager class to interact with Metabase and I wish to write a decorator inside this class to renew sessions if they're expired.

import requestsfrom datetime import datetime, timedeltafrom typing import Callable, Anyfrom urllib.parse import urljoinfrom django.utils import timezonefrom rest_framework import statusfrom core.service.logger import exceptionErrorLogfrom core.service.sensetiveApi.parameterModel import Parameterclass MetabaseManager:"""    A manager class for interacting with Metabase    Usage:        metabaseManager = MetabaseManager()        metabaseManager.getQuestion(questionId=34)    Attributes:        Parameter.METABASE_BASE_URL (str): The base url of the Metabase server        Parameter.METABASE_USERNAME (str): The username to authenticate with Metabase        Parameter.METABASE_PASSWORD (str): The password to authenticate with Metabase        Parameter.METABASE_SESSION_ID (str): The session id provided by Metabase (no need to manually set this value)    Methods:        getQuestion(self, questionId: int) -> dict:            Retrieves question data from Metabase"""    METABASE_SESSION_DURATION_IN_DAYS: int = 14    def __init__(self) -> None:        self.METABASE_BASE_URL: str = Parameter.objects.get(key=Parameter.METABASE_BASE_URL).value        self.METABASE_USERNAME: str = Parameter.objects.get(key=Parameter.METABASE_USERNAME).value        self.METABASE_PASSWORD: str = Parameter.objects.get(key=Parameter.METABASE_PASSWORD).value        self.METABASE_HEADERS: dict = {}    def _renewSession(self) -> None:        now: datetime = timezone.now()        url: str = urljoin(self.METABASE_BASE_URL, "/api/session/")        response: requests.Response = requests.post(            url=url,            json={"username": self.METABASE_USERNAME,"password": self.METABASE_PASSWORD,            },        )        response.raise_for_status()        sessionId: str = response.json()["id"]        Parameter.objects(key=Parameter.METABASE_SESSION_ID).update(            value=sessionId,            createdAt=now,            expireAt=now + timedelta(days=MetabaseManager.METABASE_SESSION_DURATION_IN_DAYS),            upsert=True,        )        self.METABASE_HEADERS["X-Metabase-Session"] = sessionId    @staticmethod    def _renewSessionIfExpired(function: Callable) -> Callable:        def wrapper(self, *args, **kwargs):            sessionId: Parameter = Parameter.objects.filter(key=Parameter.METABASE_SESSION_ID).first()            if (                    sessionId is None or                    sessionId.expireAt is None or                    sessionId.expireAt <= timezone.now()            ):                self._renewSession()            return function(self, *args, **kwargs)        return wrapper    @_renewSessionIfExpired    def getQuestion(self, questionId: int) -> dict:        url: str = urljoin(self.METABASE_BASE_URL, f"api/card/{questionId}/query")        response: requests.Response = requests.post(url=url, headers=self.METABASE_HEADERS)        if response.status_code == status.HTTP_401_UNAUTHORIZED:            self._renewSession()            response: requests.Response = requests.post(url=url, headers=self.METABASE_HEADERS)        data = response.json().get("data", {}).get("rows")        return data

Now this doesn't work, throwing this error:

Python 3.6.15 (default, Dec 21 2021, 12:28:02) [GCC 8.3.0] on linuxType "help", "copyright", "credits" or "license" for more information.(InteractiveConsole)>>> from core.service.metabaseManager import MetabaseManagerTraceback (most recent call last):  File "<console>", line 1, in <module>  File "/opt/project/app/core/service/metabaseManager.py", line 27, in <module>    class MetabaseManager:  File "/opt/project/app/core/service/metabaseManager.py", line 88, in MetabaseManager    def getQuestion(self, questionId: int) -> dict:TypeError: 'staticmethod' object is not callable

I can solve this if I use the decorator like this outside of the class:

MetabaseManager.getQuestion = MetabaseManager._renewSessionIfExpired(MetabaseManager.getQuestion)

This works, but isn't what I want. For one thing, I want the _renewSessionIfExpired decorator to remain private and using it like this outside of the class definition doesn't seem the right thing to do. And it's not clean to me, because it separates the usage of the decorator from the function declaration and I have to remember which functions I want to use the decorator on. How can I change this so that this works?

@_renewSessionIfExpireddef getQuestion(self, questionId: int) -> dict:    url: str = urljoin(self.METABASE_BASE_URL, f"api/card/{questionId}/query")    response: requests.Response = requests.post(url=url, headers=self.METABASE_HEADERS)    if response.status_code == status.HTTP_401_UNAUTHORIZED:        self._renewSession()        response: requests.Response = requests.post(url=url, headers=self.METABASE_HEADERS)    data = response.json().get("data", {}).get("rows")    return data

Python version: 3.6.15


Viewing all articles
Browse latest Browse all 14155

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>