1

I am string with decorators and the first use I have is to wrap a HTTP call to account for failed connections. The working code is as follows:

import requests

class Gotify:
    def __init__(self):
        self.url = "https://postman-echo.com/status/200"

    def ensure_safe_call(caller):
        def wrapper(*args, **kwargs):
            try:
                r = caller(*args, **kwargs)
                r.raise_for_status()
            except Exception as e:
                try:
                    print(f"cannot reach gotify: {e}: {r.text}")
                except NameError:
                    print(f"cannot reach gotify: {e} (the response r does not exist)")
            else:
                print("OK notified gotify of result change")

        return wrapper

    @ensure_safe_call
    def send(self, title, message):
        return requests.get(self.url)

Gotify().send("hello", "world")

This correct displays OK notified gotify of result change.

When editing this in PyCharm, I get two warning which I do not understand:

enter image description here

and

enter image description here

What do they mean in the context of my decorators (there are none when I do not use decorators)

WoJ
  • 27,165
  • 48
  • 180
  • 345
  • This question already has answers here: [python-decorators-in-classes](https://stackoverflow.com/questions/1263451) – stovfl Jul 10 '20 at 15:21

1 Answers1

0
class Gotify:
    def __init__(self):
        self.url = "https://postman-echo.com/status/200"

    def ensure_safe_call(caller):

Because ensure_safe_call is a class method, the first argument (in your case caller) is actually the self argument, the instance of the clas object--Gotify.

Hence the warning message about the Gotify object not being callable (it's not callable because you have not overridden the __call__ class method in your Gotify class)

Function ensure_safe_call lacks a positional argument -- this is because ensure_safe_call only takes in the self argument, and doesn't specify any actual input arguments (recall that caller == self given the way you have it defined). Thus, your decorator ensure_safe_call cannot wrap anything, because it's accepting no position arguments.

You need to define a positional argument

    def ensure_safe_call(self, caller):
        ...
Bobs Burgers
  • 761
  • 1
  • 5
  • 26
  • When you say "cannot wrap anything" you mean that the code is wrong? I am surprised because it behaves correctly (for `https://postman-echo.com/status/200` and `https://postman-echo.com/status/400`, which cover both cases (the `print` and the `raise`)) – WoJ Jul 10 '20 at 16:16
  • By "cannot wrap anything" i mean you cannot use that function to decorate another function. The function you decorate is passed as a positional argument to the decorator function--`ensure_safe_call` is a class function, so the *very first* argument implicitly passed to it is the instance of the object (conventionally called "self")--in your case, it's called `caller`. You need to add another argument to `ensure_safe_call`, the second argument will be the function decorated. `def ensure_safe_call(self, caller):` – Bobs Burgers Jul 11 '20 at 04:14
  • Thanks for the explanations. I tried your solution and i) the warnings are gone but ii) the code does not work anymore :) (`TypeError: ensure_safe_call() missing 1 required positional argument: 'caller'`). My code in the example does work, though. So I am quite confused - especially that you clearly make a point. – WoJ Jul 11 '20 at 06:06
  • Well, to avoid this problem, you should make `ensure_afe_call` a tatic function (bring it outside the class). The way its written, it *is* a static function--moving it outide your class and keeping the signature `ensure_safe_call(caller)` will give you the result you're looking for. In my experience, I've never seen a decorator as a class function before. I've only seen them as static functions – Bobs Burgers Jul 11 '20 at 17:04