2

i am trying to wrap the read and write operation of an instance of a file object (specifically the readline() and write() methods).

normally, i would simply replace those functions by a wrapper, a bit like this:

def log(stream):
    def logwrite(write):
        def inner(data):
            print 'LOG: > '+data.replace('\r','<cr>').replace('\n','<lf>')
            return write(data)
        return inner
    stream.write = logwrite(stream.write)

but the attributes of a file object are read-only ! how could i wrap them properly ?

(note: i am too lazy to wrap the whole fileobject... really, i don't want to miss a feature that i did not wrap properly, or a feature which may be added in a future version of python)

more context :

i am trying to automate the communication with a modem, whose AT command set is made available on the network through a telnet session. once logged in, i shall "grab" the module with which i want to communicate with. after some time without activity, a timeout occurs which releases the module (so that it is available to other users on the network... which i don't care, i am the sole user of this equipment). the automatic release writes a specific line on the session.

i want to wrap the readline() on a file built from a socket (cf. socket.makefile()) so that when the timeout occurs, a specific exception is thrown, so that i can detect the timeout anywhere in the script and react appropriately without complicating the AT command parser...

(of course, i want to do that because the timeout is quite spurious, otherwise i would simply feed the modem with commands without any side effect only to keep the module alive)

(feel free to propose any other method or strategy to achieve this effect)

Adrien Plisson
  • 22,486
  • 6
  • 42
  • 73
  • Since you only need readline() and write(), why worry about "a feature that i did not wrap"? Please explain what mysterious other features will be used. – S.Lott Oct 25 '10 at 10:56
  • 1
    When you're wrapping things like file objects, you should always worry about wrapping all of the relevant API calls: generally assume that any method might be called, either directly or indirectly, or you're setting yourself up for obscure bugs later on. In this case, that could translate to "certain API calls don't time out". If you don't want to implement wrappers for all methods, override them with a method that throws NotImplemented, so any unexpected method calls are noticed immediately. – Glenn Maynard Oct 25 '10 at 11:18
  • Why don't you just subclass file? – John La Rooy Oct 25 '10 at 11:20
  • @gnibbler: because i am not the one who creates the file object. i use `socket.makefile()` to get a file object from an existing socket. (but i may not be aware of a way to subclass an existing file object...) – Adrien Plisson Oct 25 '10 at 11:29
  • @gnibbler: Subclassing file is generally a bad idea. It means it'll only work on actual files (not all file-like objects), only on files that you open yourself (not files opened by external code), and Python3 does away with the `file` class entirely (eg. *all* files are opened by external code). – Glenn Maynard Oct 25 '10 at 11:31
  • This [question](https://stackoverflow.com/questions/3118929/implementing-the-decorator-pattern-in-python) may help you. You can use the accepted answer to wrap the whole file object, but only implement the methods you want to decorate. Methods you do not decorate explicitly will get forwarded to the inner object automatically. – Björn Pollex Oct 25 '10 at 10:58

1 Answers1

3

use __getattr__ to wrap your file object. provide modified methods for the ones that you are concerned with.

class Wrapped(object):
    def __init__(self, file_):
        self._file = file_

    def write(self, data):
        print 'LOG: > '+data.replace('\r','<cr>').replace('\n','<lf>')
        return self._file.write(data)

    def __getattr__(self, attr):
         return getattr(self._file, attr)

This way, requests for attributes which you don't explicitly provide will be routed to the attribute on the wrapped object and you can just implement the ones that you want

logged = Wrapped(open(filename))
Community
  • 1
  • 1
aaronasterling
  • 68,820
  • 20
  • 127
  • 125
  • Interestingly I was just trying to use this with only the `__init__()` and `__getattr__()` methods defined (as a preliminary test) but it didn't work in one place that was trying to turn it into a `list`: `TypeError: 'Wrapped' object is not iterable`. I can avoid the error by adding an `__iter__()` method that just returns `self._file`, but I believe that would side-step any other methods/processing I might eventually define. Any idea why having to explicitly define an `__iter__()` method was necessary? – martineau Nov 26 '18 at 00:50