Note that there are some explanatory texts on larger screens.

plurals
  1. POHow to decorate (monkeypatch...) a Python class with methods from another class?
    text
    copied!<p>Both <code>httplib.HTTPMessage</code> and <code>email.message.Message</code> classes[1] implements methods for RFC822 headers parsing. Unfortunately, they have different implementations[2] and they do not provide the same level of functionality.</p> <p>One example that is bugging me is that:</p> <ul> <li><p><code>httplib.HTTPMessage</code> is missing the <code>get_filename</code> method present in <code>email.Message</code>, that allows you to easily retrieve the filename from a <code>Content-disposition: attachment; filename="fghi.xyz"</code> header;</p></li> <li><p><code>httplib.HTTPMessage</code> has <code>getparam</code>, <code>getplist</code> and <code>parseplist</code> methods but AFAIK, they are not and cannot be used outside of the <code>content-type</code> header parsing;</p></li> <li><p><code>email.Message</code> has a generic <code>get_param</code> method to parse any RFC822 header with parameters, such as <code>content-disposition</code> or <code>content-type</code>.</p></li> </ul> <p>Thus, I want the <code>get_filename</code> or <code>get_param</code> methods of <code>email.message.Message</code> in <code>httplib.HTTPMessage</code> but, of course, I can't patch <code>httplib.HTTPMessage</code> as it's in the standard library... :-q</p> <p>And finally, here comes the decorator subject... :-)</p> <p>I succesfully created a <code>monkeypatch_http_message</code> function to decorate an <code>httplib.HTTPMessage</code> with my missing parsing methods:</p> <pre><code>def monkeypatch_http_message(obj): from email import utils from email.message import ( _parseparam, _unquotevalue, ) cls = obj.__class__ # methods **copied** from email.message.Message source code def _get_params_preserve(self, failobj, header): ... def get_params(self, failobj=None, header='content-type', unquote=True): ... def get_param(self, param, failobj=None, header='content-type', unquote=True): ... def get_filename(self, failobj=None): ... # monkeypatching httplib.Message cls._get_params_preserve = _get_params_preserve cls.get_params = get_params cls.get_param = get_param cls.get_filename = get_filename </code></pre> <p>Now I can do:</p> <pre><code>import mechanize from some.module import monkeypatch_http_message browser = mechanize.Browser() # in that form, browser.retrieve returns a temporary filename # and an httplib.HTTPMessage instance (tmp_filename, headers) = browser.retrieve(someurl) # monkeypatch the httplib.HTTPMessage instance monkeypatch_http_message(headers) # yeah... my original filename, finally filename = headers.get_filename() </code></pre> <p>The issue here is that I literally copied the decorating methods code from the source class, which I'd like to avoid.</p> <p>So, I tried decorating by referencing the source methods:</p> <pre><code>def monkeypatch_http_message(obj): from email import utils from email.message import ( _parseparam, _unquotevalue, Message # XXX added ) cls = obj.__class__ # monkeypatching httplib.Message cls._get_params_preserve = Message._get_params_preserve cls.get_params = Message.get_params cls.get_param = Message.get_param cls.get_filename = Message.get_filename </code></pre> <p>But that gives me:</p> <pre><code>Traceback (most recent call last): File "client.py", line 224, in &lt;module&gt; filename = headers.get_filename() TypeError: unbound method get_filename() must be called with Message instance as first argument (got nothing instead) </code></pre> <p>I'm scratching my head now... how can I decorate my class without literally copying the source methods ?</p> <p>Any suggestions ? :-)</p> <p>Regards,</p> <p>Georges Martin</p> <hr> <ol> <li><p>In Python 2.6. I can't use 2.7 nor 3.x in production.</p></li> <li><p><code>httplib.HTTPMessage</code> inherits from <code>mimetools.Message</code> and <code>rfc822.Message</code> while <code>email.Message</code> has its own implementation.</p></li> </ol>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload