0

If I "return None" explicitly for exceptions in a regular .py file, when I do the test.py file, is it correct to test with a "assert function(argument) is None?" I did a pytest and it worked fine, but when I did a CS50 check, it gave me:

":( correct fuel.py passes all test_fuel checks
expected exit code 0, not 1"

If I remove the test cases under test_convert_invalid(), CS50 did not give me the exist code 0 prompt, but it gave errors on checks for "ValueError" and "ZeroDivisionError".

Is my assert wrong under def test_convert_invalid()? If it is, why does the test file passed with pytest? How can I fix the issue?

I returned "None" explicitly because return False will be equivalent to 0, which will result in "E" (Empty tank).

`fuel.py`

import sys


def main():
    while True:
        try:
            # convert() expects a str and returns an int
            percentage = convert(input("Fraction: "))

            # gauge() expects an int and returns a str
            print(gauge(percentage))
        except:
            continue
        else:
            sys.exit()


def convert(fraction):
    try:
        x, y = fraction.split("/")

        if not x[:].isdigit() or not y[:].isdigit():
            raise TypeError

        if y == "0":
            raise ZeroDivisionError

        if int(x) > int(y) and int(y) != 0:
            raise ValueError

    except TypeError:
        return None
    except ZeroDivisionError:
        return None
    except ValueError:
        return None
    else:
        return int((float(x) / float(y)) * 100)


def gauge(percentage):
    if 0 <= percentage <= 1:
        return "E"
    elif percentage >= 99:
        return "F"
    else:
        return str(percentage) + "%"


if __name__ == "__main__":
    main()



`test_fuel.py`

from fuel import convert, gauge

def test_convert_valid():
    assert convert("3/4") == 75
    assert convert("100/100") == 100
    assert convert("99/100") == 99
    assert convert("0/100") == 0
    assert convert("1/100") == 1


def test_convert_invalid():
    assert convert("1/0") is None
    assert convert("4/3") is None
    assert convert("test") is None
    assert convert("t/1") is None
    assert convert("1/t") is None


def test_gauge():
    assert gauge(0) == "E"
    assert gauge(1) == "E"
    assert gauge(100) == "F"
    assert gauge(99) == "F"
    assert gauge(75) == "75%"

1 Answer 1

0

When there is an Exception, you want to return the exception, so your code can handle it properly. You don't want to return None; that is meaningless to the calling function. Google try/except/raise. You should find examples like this:

try:
    1 or more statements
except:
    raise # passes the exception back

A bare raise statement re-raises the current exception in the exception handler, so it can handled further up the call stack (e.g. the calling function). See these SO articles:

When trying to learn a new code feature, I write simple examples to understand the behavior. Take a look at this. It shows how a bare raise statement in function() is re-raised to main(). Once you understand how it works, you can adapt to your project. Good luck!

def main():
    test_list = [('1', '2'), ('e', '2'), ('1', '0'), ('4.4', '2')  ]
    for x,y in test_list:
        try:
            z = function(x,y)
            print(f'For x={x} and y={y}; z={z}\n')
        except ValueError:
            print(f'For x={x} and y={y}; ValueError raised by function\n')
        except ZeroDivisionError:
            print(f'For x={x} and y={y}; ZeroDivisionError raised by function\n')       
            
def function(x,y):
    try:
        z = int(x)/int(y)
        return z
    except Exception as e:
        print(f'Exception raised in function: {e}')
        raise # raises the exception to calling function -> main()

if __name__ == "__main__":
    main()
    
3
  • thanks for the suggestions. I previously had the correct error message printed for each type of errors (e.g. TypeError, print("Enter a fraction with two integer"), but the CS50 problem requires us not to do any print statement, but rather re-prompt the user for an input if what they either didn't meet the input specifications. I also am aware by default, it'll return "None" if the exception is not specified, I explicitly returned "None" as I'll need some action under "exceptions", I guess another alternative is to use "pass". Will keep digging for a fix. Thanks.
    – TimL
    May 26 at 6:56
  • The code in my answer does not return None. The bare raise statement re-raises the exception from the try/except. So if your statement was x = 1./0., it would throw a ZeroDivisionError exception, pass it to the except block which will raise that exception back to the calling program. Try it and see. (FYI, I am also doing CS50 for Python, so have done these psets.)
    – kcw78
    May 26 at 12:33
  • If anyone else comes across this, follow the pytest documentation for checking for exceptions. docs.pytest.org/en/latest/how-to/… Jun 15 at 17:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .