0

I have 15 try except statements in a for loop in the code. I think that it can be generalized but I am not able to get to the solution. How can I do it.

for item in results:

    try:
        a = item.something()
    except:
        a = ""

    try:
        b = item.something2()
    except:
        b = ""

    b = re.sub(r'\W+', ' ', b)
    b = b.strip()

    try:
        c = item.something3()
        parsed_url = urlparse(b)
        if not bool(parsed_url.scheme):
            break
    except:
        c = ""

    try:
        d = item.something4()
    except:
        d = ""

As suggested in the answer I generalized it to

def attrs(self, call_fun):
    try:
        return call_fun()
    except:
        return ""

But when I call

   a = self.attrs(item.get("data-a")) 

I get a blank output whereas calling try: except: gives correct output. Whats going wrong?

raj247
  • 381
  • 1
  • 4
  • 21
  • You may find this useful: [Multiple try codes in one block](http://stackoverflow.com/questions/17322208/multiple-try-codes-in-one-block) – Moses Koledoye Jul 06 '16 at 17:50
  • 1
    never write `except:`, always be specific as to which exception you’re catching, i.e. `except AttributeError:` – taylor swift Jul 06 '16 at 17:51

2 Answers2

2

Without changing your data:

def try_something(callable):
    try:
        return callable()
    except:
        return ""

a = try_something(item.something)

Remember that you can freely pass a callable (function or method) as a parameter to a Python function. This utilizes that. However, there may still a better way to do this by refactoring the class of item, but that would require more information.

Also, do you not know the type of exception being thrown? If you do, you should be more explicit in the except statement.

Jamie Counsell
  • 7,730
  • 6
  • 46
  • 81
1

The current accepted answer would only work if the callable did not take any arguments, to execute a small snippet and suppress a certain error it might raise it would be easier to use a context manager:

class catch:
    def __init__(self,err_type):
        valid = True
        if isinstance(err_type, tuple):
            valid = all(issubclass(e, BaseException) for e in err_type)
        else:
            valid = issubclass(err_type, BaseException)
        if not valid:
            raise TypeError("can only catch exception types, not {!r}".format(err_type))
        self.catch_err = err_type

    def __enter__(self):
        return self
    def __exit__(self,typ, err, tb):
        self.err = err #keep a reference if you want to access later.
        if isinstance(err, self.catch_err):
            return True

Then you can run your individual parts like this:

a = b = c = d = "" #default if any fail.

with catch(Exception): #should really specify what kind of exception you are catching!
    a = item.something()


with catch(Exception): #should really specify what kind of exception you are catching!
    b = item.something2()

b = re.sub(r'\W+', ' ', b)
b = b.strip()

with catch(Exception): #should really specify what kind of exception you are catching!
    c = item.something3()
    parsed_url = urlparse(b)
    if not bool(parsed_url.scheme):
        break

with catch(Exception): #should really specify what kind of exception you are catching!
    d = item.something4()

When you do a = self.attrs(item.get("data-a")) you are calling item.get("data-a") without any error checking and passing the return value into self.attrs, unless the result is callable trying to call it in self.attrs will raise a TypeError. You would need to pass a callable that takes no arguments for the accepted answer to work, this only requires you have python version 2.7+ for support for the with statement.

By the way, if item is a dict then dict.get will never raise an exception, it is in the documentation

get(key[, default])
Return the value for key if key is in the dictionary, else default. If default is not given, it defaults to None, so that this method never raises a KeyError.

Tadhg McDonald-Jensen
  • 20,699
  • 5
  • 35
  • 59
  • Well its a good explanation. But 1 thing that I observed is that the function returns even if 1 of the function throws error. i.e. if a=item.something() fails it doesnt go to b = item.something2(). Which is an undesired behaviour. b = item.something2() should run even if a = item.something() throws exception – raj247 Jul 12 '16 at 19:50
  • When the exception that is being handled is caught, the `with` block will end and execution will resume at the end of the block, so if each part is in it's own `with catch(...)` block then it should work correctly. It doesn't mean "suppress all exceptions like this in this block" it is not that much then the original `try: except:` – Tadhg McDonald-Jensen Jul 12 '16 at 20:04
  • I was getting the error issubclass() arg 1 must be a class – raj247 Jul 12 '16 at 20:55
  • sorry my fault, needed to add `typ is not None and` before the `issubclass` check or just use `isinstance(err, self.check_err)` instead. Fixed now. – Tadhg McDonald-Jensen Jul 12 '16 at 20:57
  • 1
    no no, see [my edit](http://stackoverflow.com/revisions/38336776/2) that is what you need to change it to. – Tadhg McDonald-Jensen Jul 12 '16 at 21:04