-1

In a module I am writing, I have a need for checking that incoming data matches particular requirements and formats. I thought I knew a good way to do this, but I need to add logging functionality and it made me realise I must be writing my code improperly. A MWE of what I am currently doing is:

string1 = 'Hello, World!' # represents something like a column name in an incoming dataframe
string2 = 'Hello, World?' # represents what the column name should be according to an incoming config file

if string1 != string2:
    raise ValueError('Input from string1 does not match expected value given in string2')

That works perfectly, and the raised error can be grabbed by my pytest functions as well. But it is not part of a try except sequence, and I dont see how it can be. try doesn't behave like an if and except requires the specific error to actually be raised first.

This poses a problem because I dont know how to capture the raised error with python's logging module without needless code duplication. The best I can do is:

import logging 

string1 = 'Hello, World!' # represents something like a column name in an incoming dataframe
string2 = 'Hello, World?' # represents what the column name should be according to an incoming config file

if string1 != string2:
    logging.error('Input from string1 does not match expected value given in string2')
    raise ValueError('Input from string1 does not match expected value given in string2')

But that is two, nearly identical lines to do the same thing. logging.error doesnt actually halt the code, which I need to happen, and I cant find a way to capture the raise ValueError(...) with logging.

Obviously, with this example, logging is just going to print to the terminal, but ultimately, it will be printing to a file (and I know how to do that).

What is the best way to write such a test of variables to have both logging and raised error functionality?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Alex Howard
  • 319
  • 3
  • 13
  • It's unclear what exactly the problem is here. No the `ValueError` is not part of any `try` and `except` _inside that function_, but it it's caught in the same function it was raised that's generally a bit pointless. In that case the function's responsibility is just to throw an error and it can expect the caller (or somewhere else up the chain) to deal with it. Or if the function's responsibility is both to log _and_ throw, then your second snippet is correct (those lines _don't_ do the same thing - one logs, one throws - but you could factor out the repeated message). – jonrsharpe Feb 03 '22 at 17:30
  • Alternatively if you're talking about capturing errors globally, so you can log anything that goes uncaught, see e.g. https://stackoverflow.com/a/6598286/3001761 – jonrsharpe Feb 03 '22 at 17:39

2 Answers2

1

The idea is to put the raise in a try block and do the logging in the except. Here's how you'd do it for this small example:

string1 = 'Hello, World!'
string2 = 'Hello, World?'

try:
    if string1 != string2:
        raise ValueError('Input from string1 does not match expected value given in string2')
except ValueError as e:
    logging.error(e)
    raise

In real life, it tends to look more like:

try:
    do_some_complex_thing()
except Exception as e:
    log.error(e)

where do_some_complex_thing() might have a lot going on inside of it -- by wrapping it all in one outer try block you can make sure that any otherwise-unhandled exception is logged in a single spot.

Samwise
  • 68,105
  • 3
  • 30
  • 44
  • You _can_, but _should_ you? The error gets swallowed so all you've achieved with the `try`/`except` machinery is move a string two lines. – jonrsharpe Feb 03 '22 at 17:35
  • In a small example with a single error it's not useful, no; the value comes when you have a bunch of places that might raise errors and you want to funnel them all through one set of logging code. – Samwise Feb 03 '22 at 17:37
0

I'm not very familiar with the logging module but this might help:

Exceptions in python are just objects, and you can generate them without raising them immediately.

err = ValueError('Input from string1 does not match expected value given in string2')
# log the error like you would in an except block and then
raise err
Joe
  • 331
  • 1
  • 7