0

In the over-simplifed example, say the original class, has many methods, all of them returns lower case strings. I want to wrap the Original class so that after calling its methods, follow by .upper()

class Original(object):
    def get_assets(self):
        # returns lower case string

class Wrap(Original):
    # code that wraps Original's methods

So that calling Wrap().get_assets() is equivalent to Original().get_assets().upper()

In a related but separate question, say, if Original's methods return json strings, how do I wrap it so that the result will be called with json.loads()?

class Original(object):
    def get_assets(self):
        # returns json string

class Wrap(Original):
    # code that wraps Original's methods

So that calling Wrap().get_assets() is equivalent to json.loads(Original().get_assets())

Background

I came across this snipeit python library, but the every method requires me to specify url and token, so I have the following already to tackle the problem, but I haven't figured out how to solve the json string problem.

def snipeit_api_factory(kls):
    class Cls(kls):
        def __getattribute__(self, item):
            attr = kls.__getattribute__(self, item)
            if isinstance(attr, types.MethodType):
                return partial(attr, settings.SNIPEIT_URL, settings.SNIPEIT_TOKEN)
            return attr
    return Cls()

ANSWER

Since this question is closed, I will post my answer here:

def snipeit_api_factory(kls):
    """
    return an instance of the wrapped snipe-it API class to avoid specifying url and token for every method, and jsonify the result
    """
    def jsonify(func):
        def wrapper(*args, **kwargs):
            return json.loads(func(*args, **kwargs))
        return wrapper

    class Cls(kls):
        def __getattribute__(self, item):
            attr = kls.__getattribute__(self, item)
            if isinstance(attr, types.MethodType):
                return jsonify(partial(attr, settings.SNIPEIT_URL, settings.SNIPEIT_TOKEN))
            return attr
    return Cls()
James Lin
  • 25,028
  • 36
  • 133
  • 233
  • 1
    I would probably use a decorator for that that inspect all the methods and replace them by `return super().method(*args, **kwargs).upper()` – Adirio Sep 03 '20 at 09:25
  • 1
    Or write a `__getattribute__` implementation that proxies to the `super`class attribute? – jonrsharpe Sep 03 '20 at 09:26
  • while you extending the class - use super() to inherit the main-class methods. check this doc https://rhettinger.wordpress.com/2011/05/26/super-considered-super/ – Artem Fedotov Sep 03 '20 at 09:28
  • Be careful with `__getattribute__` as @jonrsharpe suggested as not all attributes are methods. – Adirio Sep 03 '20 at 09:29
  • well yes, I will have to check if they are `callable()` first – James Lin Sep 03 '20 at 09:30
  • @JamesLin that's one way, perhaps more principled is to use `importy types; isinstance(x, types.MethodType)`. In principle, an attribute can be callable without being a method. note, be careful with implementing `__getattribute__`, it will recurse if you try to do something like `self.foo` inside of it! So use `super().__getattribute__('foo')` – juanpa.arrivillaga Sep 03 '20 at 09:38
  • @JamesLin bear in mind that additional warning I added too! – juanpa.arrivillaga Sep 03 '20 at 09:39
  • 1
    Yeah already been over that, `attr = Original.__getattribute__(self, item)` – James Lin Sep 03 '20 at 09:40

0 Answers0