0

I have some python module, which has a class ModuleClass and I can't modify that class.

Now, I want to be able to proxify the method calls and add certain logging features. I assume this should be done via forwarding object and the corresponding proxy (following Effective Java, Item 16).

The python pseudocode I came up with follows.

(Sorry, I'm really bad at python and I would appreciate if you could point out errors here).

# This is what I've got in my module and this code cannot be changed.
class ModuleClass(object):
    def method1(self):
        # Some implementation
        pass()
    def method2(self):
        # Some implementation
        pass()

# Simple forwarding proxy to avoid the situation described in Effective Java, I16

# However, in Java this class would usually be extending the interface, not
# inheriting 'ModuleClass' (I'm confused and don't know how to do the same
# in python).

class ForwardingModuleClass(ModuleClass):
    # 'proxifiedObject' is 
    def __init__(self, proxifiedObject):
        self.proxifiedObject = proxifiedObject

    # Overriding the first method
    def method1(self):
        return self.proxifiedObject.method1()

    # Same for method2...

class LoggingModuleClass(ForwardingModuleClass):
    # 'classThatActuallyDoesStuff' should be an instance of 'ModuleClass'.
    def __init__(self, classThatActuallyDoesStuff):
        # Sorry for my bad knowledge of python syntax, but
        # I assume I can initialize the superclass here using
        # the supplied 'ModuleClass' instance.
        super(classThatActuallyDoesStuff)

    # Overriding the first method.
    def method1(self):
        print("Yay! This 'method1' really logs something!")
        return super.method1()

    # Overriding the second method.
    def method2(self):
        print("Yay!!!! This 'method2' also does something cool!")
        return super.method2()

Now, I guess, properly written, this would work and I would have the logging proxy for my initial ModuleClass.

If there are errors or if this is not pythonish, please point it out.

Also, I suspect this can easily be done using decorators, but, unfortunately, I can't figure the appropriate way and I have no idea what might happen if ModuleClass already has some method decorators.

Could you help me here too?

Yippie-Ki-Yay
  • 22,026
  • 26
  • 90
  • 148
  • Forget about Java when writing Python. The semantics of the two aren't even close to each other. – Cat Plus Plus Nov 15 '11 at 22:08
  • @CatPlusPlus Okay, but I guess then there should be a nice way to solve this in `python`. – Yippie-Ki-Yay Nov 15 '11 at 22:14
  • 1
    Python error: super is not an object but a method. To call parents constructor you have to call super().__init__() and to call parents method you should call super().method() (I suppose you are using python 3...) – gecco Nov 15 '11 at 23:16

3 Answers3

2

How about just subclass ModuleClass directly:

import logging

logger=logging.getLogger(__name__)

class LoggingModuleClass(ModuleClass):
    def method1(self):
        logger.info("Yay! This 'method1' really logs something!")
        return super(LoggingModuleClass,self).method1()
    def method2(self):
        logger.info("Yay! This 'method2' also does something cool!")
        return super(LoggingModuleClass,self).method2()

logging.basicConfig(level=logging.DEBUG)

(I added code to show a basic setup for logging the Python way).

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • `class LoggingModuleClass`, not `def`. – Cat Plus Plus Nov 15 '11 at 22:11
  • Yeah, this is what the `Effective Java` talks about. Sometimes, if you call the `method2`, which implementation implicitly calls `method1`, you'll get unwanted messages in your log *(well, at least in my case, they're unwanted)*. That's the purpose of the forwarding class. **However, thank you, and maybe if you know the solution with decorators, you might help me here too.** – Yippie-Ki-Yay Nov 15 '11 at 22:13
  • @unutbu Yeah, I suspect `logging` package would probably do fine. But (just being curious), for example, if I want to add benchmarking the same way as described in the question - is it possible with decorators? – Yippie-Ki-Yay Nov 15 '11 at 22:16
2

If you really want a wrapper, then just write it, with no subclassing and intermediate classes.

class LoggingFoo(object):
    def __init__(self, *args, **kwargs):
        self.obj = Foo(*args, **kwargs)

    def method1(self):
        # ...
        return self.obj.method1()

    def method2(self):
        # ...
        return self.obj.method2()
Cat Plus Plus
  • 125,936
  • 27
  • 200
  • 224
  • And so, due to python semantics, any instance of this class can be passed to the function where `ModuleClass` is expected? – Yippie-Ki-Yay Nov 15 '11 at 22:20
  • Does python, by any chance, has functions which with contracts like `the instance of the passed class should be a 'ModuleClass'`? Or is this java talking inside me? – Yippie-Ki-Yay Nov 15 '11 at 22:21
  • @Yippie-Kai-Yay: Python uses duck typing, interfaces are implicit (if an object has a method X, it can be used anywhere where method X is needed). You can use `isinstance` to assert the type, but generic code should rather avoid it. – Cat Plus Plus Nov 15 '11 at 22:22
  • Thank you very much. Do I realize correctly that this way to make a proxy doesn't break the existing decoration of the `method1`, `method2` if it's present? And would this work all the time or there are some corner cases except `isinstance`, when this might fail? – Yippie-Ki-Yay Nov 15 '11 at 22:28
1

If you are looking for a decorator solution then you should have a look to the answer of this post: Python decorator makes function forget that it belongs to a class

You indicated that you want to avoid "unwanted messages" (in case method1 calls method2), then I'd suggest you to chose the solution provided by Cat Plus Plus, else I'd go for operators.

Community
  • 1
  • 1
gecco
  • 17,969
  • 11
  • 51
  • 68