Martin
Martin

Reputation: 1187

How to detect an error condition in functions called from the main function?

I have a small program which calls multiple functions from the main() function and in each of the functions there is a small probability, that an error occurs. In order to be compatible with the existing monitoring system, I need to make a file to /var/tmp/error_triggered/ directory if there was at least one error or remove the file from this directory if there were no errors. One way to do this is to use variable with global scope:

#!/usr/bin/env python3

import os
import random
from pathlib import Path

def f1():
  global error
  for i in range(1, 5):
    if random.randint(0, 9) == 0:
      error = 1

def f2():
  global error
  for i in range(1, 5):
    if random.randint(0, 9) == 0:
      error = 1

def main():
  f1()
  f2()

if __name__ == '__main__':
  error = 0
  main()
  if error == 1:
    Path('/var/tmp/error_triggered/' + os.path.splitext(os.path.basename(__file__))[0]).touch()
  else:
    Path('/var/tmp/error_triggered/' + os.path.splitext(os.path.basename(__file__))[0]).unlink(missing_ok=True)

It is usually a bad practice to modify a global variable from inside functions. Is it justified here? Or is there a better approach to solve this problem?

Upvotes: 1

Views: 55

Answers (3)

houseofleft
houseofleft

Reputation: 447

I'd echo what some of the others have raised here, that raising exceptions is the appropriate response to errors but add that embedding a try/except loop if you need specific error behaviour:

def a():
    if random.randint(0, 9) == 0:
        raise Exception("error!")

def main():
    f = Path('/var/tmp/error_triggered/' + os.path.splitext(os.path.basename(__file__))[0])
    try:
        for i in range(0,5):
            a()
        f.unlink(missing_ok=True)
    except Exception as e:
        print(e)
        f.touch()
   

I'm assuming your code is a bit more complicated that raising errors at random, but the principles the same- if something hits and error, it should raise an exception, if you need to handle the exception in a way that isn't just leaving the shell, use a try/except loop.

Upvotes: 1

Samwise
Samwise

Reputation: 71477

The standard way of handling errors in Python is raising exceptions:

def f1():
    for i in range(1, 5):
        if random.randint(0, 9) == 0:
            raise ValueError()

def f2():
    for i in range(1, 5):
        if random.randint(0, 9) == 0:
            raise ValueError()

def main():
    f = Path('/var/tmp/error_triggered/' + os.path.splitext(os.path.basename(__file__))[0])
    try:
        f1()
        f2()
        f.unlink(missing_ok=True)
    except ValueError:
        f.touch()

There's a subtle difference between this and what you were doing: if f1 raises an exception, f2 won't be called (that is, raising an exception immediately stops the try block and goes straight to the except). This is desirable behavior more often than not, since usually if an error happens you don't want to keep going with what you were doing.

If for some reason you want f2 to be called even if f1 has already produced an error, then it might be more appropriate to return the error the same way you'd return any other value:

def f1():
    for i in range(1, 5):
        if random.randint(0, 9) == 0:
            return 1
    return 0

def f2():
    for i in range(1, 5):
        if random.randint(0, 9) == 0:
            return 1
    return 0

def main():
    f = Path('/var/tmp/error_triggered/' + os.path.splitext(os.path.basename(__file__))[0])
    if f1() + f2():
        f.touch()
    else:
        f.unlink(missing_ok=True)

Using a global is almost never the best way to pass information between functions. For anything beyond the simplest cases it will just make your code very difficult to debug.

Upvotes: 1

Pratik Thanki
Pratik Thanki

Reputation: 252

I'd suggest creating an ERROR_COUNTER int variable or dictionary which stores the counts, something like:

{ functionname: count, functionname2: count }

Which is updated with every occurence of an error.

Making variables global from inside functions is not great.

I don't know your use case/context specifically but you could also return an int of errors with each function call if all they do is perform an action. This would make the caller responsible for handling the count of errors than individual functions.

Upvotes: 1

Related Questions