1

There are ways how programmer can make programming and refactoring easier and more simple, python is very good in this area.

I'm curious whether is there a more elegant way to solve my problem than brute-force writing the same code multiple times again and again.

Situation:

I'm writing a code. There are many equal methods calling with different arguments sequentially.

For example - I have this code:

...
...
my_method(1)
my_method(2)
my_method(3)
my_method(4)
...
my_method(10)
...

So I have this code written, everything works fine but suddenly I find out that I need to make a log file so I have to put try-except on everyone of this methods so the code will look like this:

...
...
try:
   my_method(3)
except Exception as e:
   print_to_file(log.txt,str(e))
...
...
try:
   my_method(8)
except Exception as e:
   print_to_file(log.txt,str(e))
...
...

Do I have a better option than changing every my_method(x) calling and putting it into try-except clause? I know that it is a mistake of the programmer who had to think about it at the beginning but these situations happens.

EDIT: According to the answer - the code above is the simple example. In real code there are no int arguments given but dates where there is no logic there so I can't put it into the loop. Assume that the arguments can't be generated.

Milano
  • 18,048
  • 37
  • 153
  • 353

3 Answers3

1

If you're using the logger supplied by python, you can redirect exception output to the log as opposed to have to put a ton of try blocks everywhere:

import os, sys
import logging
logger = logging.getLogger(__name__)
handler = logging.StreamHandler(stream=sys.stdout)
logger.addHandler(handler)

def handle_exception(exc_type, exc_value, exc_traceback):
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return

    logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))

sys.excepthook = handle_exception

if __name__ == "__main__":
    raise RuntimeError("Test unhandled")

Now is an exception is thrown, you won't need a try block, it will be written to the log regardless

ref

Community
  • 1
  • 1
Syntactic Fructose
  • 18,936
  • 23
  • 91
  • 177
0

You can take advantage of the fact that a function, in python, is totally an object, and write a function that takes in another function, runs it, and logs any exceptions

def sloppyRun(func, *args, **kwargs):
    """Runs a function, catching all exceptions
    and writing them to a log file."""
    try:
        return func(*args, **kwargs) #running function here
    except:
        logging.exception(func.__name__ + str(args) + str(kwargs))
        #incidentally, the logging module is wonderful. I'd recommend using it.
        #It'll even write the traceback to a file. 

And then you can write something like

sloppyRun(my_method, 8) #note the lack of parens for my_method
NightShadeQueen
  • 3,284
  • 3
  • 24
  • 37
0

You could have like a context manager or a decorator to log what you need, when you need to. if you intend to always log an exception when you use that function, I would suggest going the simple decorator rule or even a try and except inside that function. If it is functions not in your code or you dont want them to always log,then I would used a context manager (called as with ..:)

A context manager example code

import functools

class LoggerContext():
    def __enter__(self):
        # function that is called on enter of the with context
        # we dont need this
        pass

    def __exit__(self, type, value, traceback):
        # If there was an exception, it will be passed to the
        # exit function. 
        # type = type of exception
        # value = the string arg of the exception
        # traceback object for you to extract the traceback if you need to
        if traceback:
            # do something with exception like log it etc
            print(type, value, traceback)

        # If the return value of the exit function is not True, python
        # interpreter re-raises the exception. We dont want to re-raise
        # the exception
        return True

    def __call__(self, f):
        # this is just to make a context manager a decorator
        # so that you could use the @context on a function
        @functools.wraps(f)
        def decorated(*args, **kwds):
            with self:
                return f(*args, **kwds)
        return decorated

@LoggerContext()
def myMethod(test):
    raise FileNotFoundError(test)

def myMethod2(test):
    raise TypeError(test)

myMethod('asdf')

with LoggerContext():
    myMethod2('asdf')

A simple decorator example:

import functools

def LoggerDecorator(f):
    @functools.wraps(f)
    def decorated(*args, **kwds):
        try:
           return f(*args, **kwds)
        except Exception as e:
           # do something with exception
           print('Exception:', e)
    return decorated

@LoggerDecorator
def myMethod3(test):
    raise IOError(test)

myMethod3('asdf')
ashwinjv
  • 2,787
  • 1
  • 23
  • 32