Wrapping a method from outside the class in Python with decorator

Question:

I would like to ask a question, how I can override/extend the existing external python class. I wanted to be able to call the same API, like parent class, but with some modifications.

Something like that:

[a]
b = 1
import configparser

# my wrapper class
class MyConfigParser(configparser.ConfigParser):
    # override/wrap the parent class with all arguments (could be some optionals too) parent function has the same name, but it shouldn't be called
    def getint(self, section, option):
        # call the parent function, pass all args (maybe without mentioning them, ideally without copying them)
        ret = parent.get(section, option)
        # make modifications
        print("my wrapper")
        ret = ret + 1
        # return modified value
        return ret

config = MyConfigParser()
# call parent function
config.read('my-file.ini')
# call my wrapped-function
a = config.getint('a','b')

Just for an information, I’m unable to modify the parent class.
How to do that?

Asked By: Andy

||

Answers:

I think you are confused about what a decorator is and what the word wrapping typically refers to. Neither of those apply here, if I understood your intent correctly.

Of course you can subclass some existing class that you may or may not have any control over and override its methods. And of course you can conveniently call the parent class’ methods from inside your own methods. In Python you typically use the built in super proxy class for that. But none of this has anything to do with decoration or wrapping.

If you want to override a superclass’ method, you should take care to match its signature. Otherwise you would be violating type safety. If all you care about are a few specific arguments but not all of them, you can cheat a bit and collect the rest in the catch-all *args, **kwargs parameters, but you should then pass them along to the superclass’ method call.

Also, you seemed to be calling the ConfigParser.get method inside your overridden getint method. But I am pretty sure that was a typo/mistake because you are clearly expecting the returned value that you assign to ret to be an int, whereas get returns a str. So you should be calling super().getint(...) there.

Finally, if we are already at it, it is good form to properly annotate your functions with types. (see PEP 484 for details)

Here is what I think you need:

from configparser import ConfigParser
from typing import Any


class MyConfigParser(ConfigParser):
    def getint(self, section: str, option: str, *args: Any, **kwargs: Any) -> int:
        ret = super().getint(section, option, *args, **kwargs)
        print("overridden `getint`")
        ret += 1
        return ret  # type: ignore[no-any-return]


if __name__ == "__main__":
    config = MyConfigParser()
    config.read("my-file.ini")
    print(config.getint("a", "b"))

The output with your example ini-file:

overridden `getint`
2

There are a lot of questions and answers on this platform about super and subclassing as well as decorators. I would suggest you search a bit and read up on those.

Answered By: Daniil Fajnberg
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.