views:

169

answers:

3

I want to wrap the default open method with a wrapper that should also catch exceptions. Here's a test example that works:

truemethod = open
def fn(*args, **kwargs):
    try:
        return truemethod(*args, **kwargs)
    except (IOError, OSError):
        sys.exit('Can\'t open \'{0}\'. Error #{1[0]}: {1[1]}'.format(args[0], sys.exc_info()[1].args))

open = fn

I want to make a generic method of it:

def wrap(method, exceptions = (OSError, IOError)):
    truemethod = method
    def fn(*args, **kwargs):
        try:
            return truemethod(*args, **kwargs)
        except exceptions:
            sys.exit('Can\'t open \'{0}\'. Error #{1[0]}: {1[1]}'.format(args[0], sys.exc_info()[1].args))

    method = fn

But it doesn't work:

>>> wrap(open)
>>> open
<built-in function open>

Apparently, method is a copy of the parameter, not a reference as I expected. Any pythonic workaround?

+2  A: 

Try adding global open. In the general case, you might want to look at this section of the manual:

This module provides direct access to all ‘built-in’ identifiers of Python; for example, __builtin__.open is the full name for the built-in function open(). See chapter Built-in Objects.

This module is not normally accessed explicitly by most applications, but can be useful in modules that provide objects with the same name as a built-in value, but in which the built-in of that name is also needed. For example, in a module that wants to implement an open() function that wraps the built-in open(), this module can be used directly:

import __builtin__

def open(path):
    f = __builtin__.open(path, 'r')
    return UpperCaser(f)

class UpperCaser:
    '''Wrapper around a file that converts output to upper-case.'''

    def __init__(self, f):
        self._f = f

    def read(self, count=-1):
        return self._f.read(count).upper()

    # ...

CPython implementation detail: Most modules have the name __builtins__ (note the 's') made available as part of their globals. The value of __builtins__ is normally either this module or the value of this modules’s __dict__ attribute. Since this is an implementation detail, it may not be used by alternate implementations of Python.

Teddy
That seems a bit specific for the generic case.
foosion
Ouch, so you mean to replace that for _any_ access to the `open` function? Doesn't sound like what the OP wants at all, it's not a wrapper but a global change.
RedGlyph
@RedGlyph: Well, yes, that's how I interpreted the original question.
Teddy
it's not hard but it's evil. place `_oldopen = __builtin__.open` then `__builtin__.open = open` right in the module code quoted here, and you have replaced it everywhere. Except those who imported it by `from __builtin__ import open` before this module was imported.
kaizer.se
(ah, wait.. save old open in `_oldopen` before you implement the replacement, and call the oldone by `_oldopen` of course, to avoid recursion! :-)
kaizer.se
+1  A: 

you can just add return fn at the end of your wrap function and then do:

>>> open = wrap(open)
>>> open('bhla')
Traceback (most recent call last):
  File "<pyshell#24>", line 1, in <module>
    open('bhla')
  File "<pyshell#18>", line 7, in fn
    sys.exit('Can\'t open \'{0}\'. Error #{1[0]}: {1[1]}'.format(args[0], sys.exc_info()[1].args))
SystemExit: Can't open 'bhla'. Error #2: No such file or directory
SilentGhost
+3  A: 

The problem with your code is that inside wrap, your method = fn statement is simply changing the local value of method, it isn't changing the larger value of open. You'll have to assign to those names yourself:

def wrap(method, exceptions = (OSError, IOError)):
    def fn(*args, **kwargs):
        try:
            return method(*args, **kwargs)
        except exceptions:
            sys.exit('Can\'t open \'{0}\'. Error #{1[0]}: {1[1]}'.format(args[0], sys.exc_info()[1].args))

    return fn

open = wrap(open)
foo = wrap(foo)
Ned Batchelder
+1, that's the good solution. Except to be even more general, the message shouldn't mention "Can't open" (was already in the OP's post, I know).
RedGlyph
Ok, so basically it can serve as a decorator for my own methods.
culebrón
Ah, you're right: I missed that part...
Ned Batchelder