<jupyter_start><jupyter_text>Dealing with errors---There are various types of error that a well written function will handle but which can sometimes be forgotten.Errors can be categorised into:*  Syntax errors  errors in the code that stop it from being interpreted and run - these are often picked up by the code editor, or by Python when it tries to translate the code before running it  *  Logic errors  these are hard to pick up and rely on good testing or user feedback.  A logic error will result in 'wrong' data or functionality  *  Runtime errors  these are generally caused by operations that work perfectly well with the 'right' data but fall over if they encounter the 'wrong' data. Exercise 1  ---The code below has a `syntax` error.  Correct the error and run the code.<jupyter_code>def get_list():
  mylist = ["red",'yellow','pink',"green",'orange',"purple","blue"]
  return mylist

print(get_list())<jupyter_output>['red', 'yellow', 'pink', 'green', 'orange', 'purple', 'blue']<jupyter_text>Exercise 2 ---The code below has a `logic` error.  The function is being run by a test, which will fail on first run.  Correct the error and run the code to get the test to pass.<jupyter_code>def calculate_average(nums):
  total = 0
  for num in nums:
    total += num
  return total/len(nums)

# Test
actual = calculate_average([4,2,6,8,3,1])
expected = 4
if  actual == expected:
  print("Test passed")
else:
  print("Test failed, expected", expected, "but got", actual)<jupyter_output>Test passed<jupyter_text>Exercise 3---The code below does not have any errors and will run perfectly well, until it receives some data that doesn't fit what it is expecting.  Run the code, entering the number 16, it will crash (a runtime error) because the Python input() function always returns a string, which can't be divided.  One solution is to convert the input to an integer.  This will deal with the input problem in most cases.  Make a change to the function code to convert the input into an int.  Then run the test again, there should be no runtime error and the test should now pass.<jupyter_code>def get_user_input():
  user_input = input("Please enter a number ")
  return int(user_input)

### Test 1 - user enters 16 ###
print("Test 1 - with valid integer")
actual = get_user_input() / 2
expected = 8
if  actual == expected:
  print("Test passed")
else:
  print("Test failed, expected", expected, "but got", actual)<jupyter_output>Test 1 - with valid integer
Please enter a number 16
Test passed<jupyter_text>Exercise 4---The solution you have just implemented will solve the known problem that input() always returns a string, BUT, what if the user actually enters something that isn't a number?  Run the code below, it will crash with the input **six** but we don't want users to be informed of an input error by the program crashing! This error is not a given (like inputs always being strings) it has to be anticipated as a possibility.  In these cases - often from any form of input not being of the right type, including from the user, a file, an API call, a database read or function parameters - we want to try to handle the error and to allow the program to deal with it gracefully, instead of crashing.Most programming languages have instructions for handling runtime errors like this.  In any situation where there is potential for runtime errors, you can use:  ```try:   code to runexcept:   what to do if there is an error, to handle the problem gracefully and move on```If a runtime error occurs at any point within the `try` section, processing will switch to the `except` section and run the code there (this might report the problem to the user, then stop so that processing continues from before the try, or it might log the error, inform the user and shut the program down).  What is done in the `except` section depends on the severity of the error and its effect on the further running of the program (ie data might be corrupted at this point and so it is not advisable to continue).  In the example below, the runtime error occurs when it is trying to divide numbers that are not valid, so we will use `try: .. except: ..` in the function to ensure that a valid number is always returned and that an error message to the user is shown if the input was invalid.  For this example, if there is an invalid input we will return 0 (so we are always returning a valid input but it will result in 0 when divided so for the purposes of this example, this will do) Add a second test for invalid integersAdd a second test to the code below:This test should expect that the result is 0 when the user enters a word, such as `six`.The code below now has two tests, the first expects the user to enter 16 and the second expects the user to enter the word *six*Run the code, first enter 16 and you should see that the first test still passes, even though the code has been changed to include the `try...except`.  You will be asked to enter a number again (as there are 2 tests).  Enter `six` and you should see that this test also passes, but should see an error message letting you know that this was an invalid number.  The code is doing exactly what it should do:  ```Test 1 - with valid integerPlease enter a number 16Test passedTest 2 - with invalid integerPlease enter a number sixInvalid number, run the function againTest passed```<jupyter_code>def get_user_input():
  try:
    user_input = int(input("Please enter a number "))
    return user_input
  except:
    print("Invalid number, run the function again")
    return 0

### Test 1 - user enters 16 ###
print("Test 1 - with valid integer")
actual = get_user_input() / 2
expected = 8
if  actual == expected:
  print("Test passed")
else:
  print("Test failed, expected", expected, "but got", actual)

### Test 2 - user enters six ###
print("Test 2 - with invalid integer")
# ADD YOUR TEST CODE HERE #
actual = get_user_input()
expected = 0
if  actual == expected:
  print("Test passed")
else:
  print("Test failed, expected", expected, "but got", actual)<jupyter_output>Test 1 - with valid integer
Please enter a number 16
Test passed
Test 2 - with invalid integer
Please enter a number six
Invalid number, run the function again
Test passed<jupyter_text>Exercise 5 - add a third test---Add a Test 3, to test for the user just pressing enter and not entering anything at all) The expected result will be the same as Test 2<jupyter_code>def get_user_input():
  try:
    user_input = int(input("Please enter a number "))
    return user_input
  except:
    print("Invalid number, run the function again")
    return 0

### Test 1 - user enters 16 ###
print("Test 1 - with valid integer")
actual = get_user_input() / 2
expected = 8
if  actual == expected:
  print("Test passed")
else:
  print("Test failed, expected", expected, "but got", actual)

### Test 2 - user enters six ###
print("Test 2 - with invalid integer")
# COPY YOUR TEST CODE HERE #
actual = get_user_input()
expected = 0
if  actual == expected:
  print("Test passed")
else:
  print("Test failed, expected", expected, "but got", actual)
### Test 3 - user enters nothing ###
print("Test 3 - with empty integer")
# ADD YOUR TEST CODE HERE #
actual = get_user_input()
expected = 0
if  actual == expected:
  print("Test passed")
else:
  print("Test failed, expected", expected, "but got", actual)<jupyter_output>Test 1 - with valid integer
Please enter a number 16
Test passed
Test 2 - with invalid integer
Please enter a number six
Invalid number, run the function again
Test passed
Test 3 - with empty integer
Please enter a number 
Invalid number, run the function again
Test passed<jupyter_text>Exercise 6 - opening a file---Write a function that will return an open file if it is given the file name.  If the file doesn't exist, a message should be shown to the user and the function should return None.  If the file does exist, and is opened correctly, the function should return the file to the caller.  The code has been started for you and two tests have been written.  Run the program with the second test commented out, to test that it works if the file doesn't exist (this assumes that you don't have a file called `error.txt` in the same directory as this notebook.  Now create a text file called **valid_file.txt** in the same directory as this notebook.  Uncomment the second test and run again.  Both tests should now pass.*Note*:  The tests look a little different now - this time instead of comparing actual with expected, the test looks at if the actual result is None or not None.  This is because it is impossible to tell what result will be returned for a valid file, as this is dependent on the file and the operating system.  It is sufficient to know that the file is opened if it exists and that the program doesn't crash if it doesn't.<jupyter_code>import os

def get_file(filename):
  # add your code here to open the file and return it, or to print an error message and return None
  try:
    file = open(filename)
    return file
  except:
    print("File does not exist")
    return None

# Test 1 - try to open a file that doesn't exist
print("Test 1 file doesn't exist")
actual = get_file("error.txt")
if actual is None:
  print("Test passed")
else:
  print("Test failed, should have received None")

# Test 2 - try to open a file that does exist
print("\nTest 2 file does exist")
actual = get_file("valid_file.txt")
if actual is not None:
  print("Test passed")
else:
  print("Test failed, should have received file")<jupyter_output>Test 1 file doesn't exist
File does not exist
Test passed

Test 2 file does exist
Test passed<jupyter_text>Exercise 7 - write a test---The function in the code cell below will take two parameters (two integers), and will return the result of dividing the first by the second, rounded to 2 decimal places.  If either isn't a number, it will return -999999  (not an impossible answer but improbable).Write a test for this function that will run it with the numbers 15 and 3 and will expect the answer to be 5.<jupyter_code>def divide_nums(num1, num2):
  try:
    answer = num1 / num2
    return answer
  except:
    return -999999

# Test 1 - works with valid numbers (15 divided by 3 is 5)
print("Test 1 - works with valid numbers (15 divided by 3 is 5)")
# ADD YOUR TEST CODE HERE #
actual = divide_nums(15, 3)
expected = 5.0
if  actual == expected:
  print("Test passed")
else:
  print("Test failed, expected", expected, "but got", actual)<jupyter_output>Test 1 - works with valid numbers (15 divided by 3 is 5)
Test passed<jupyter_text>Exercise 8 - add two more tests---Add two more tests for this function:*Test 2* - Test running the function with the numbers `(20, 0.3)` and expect the answer to be `66.67`*Test 3* - Test running the function with `(20, None)` and expect the answer to be `-999999`Run the tests and then **modify the function** so that the test using the numbers `(20, 0.3)` passes.<jupyter_code>def divide_nums(num1, num2):
  try:
    answer = num1 / num2
    return round(answer, 2)
  except:
    return -999999

## COPY YOUR TEST CODE, THEN ADD THE TWO EXTRA TESTS
 
# Test 1 - works with valid numbers (15 divided by 3 is 5)
print("Test 1 - works with valid numbers (15 divided by 3 is 5)")
# ADD YOUR TEST CODE HERE #
actual = divide_nums(15, 3)
expected = 5.0
if  actual == expected:
  print("Test passed")
else:
  print("Test failed, expected", expected, "but got", actual)

# Test 2 - Test with the numbers (20, 0.3) and expect the answer to be 66.67
print("Test 2 - works with valid numbers (20, 0.3) and expect the answer to be 66.67")
# ADD YOUR TEST CODE HERE #
actual = divide_nums(20, 0.3)
expected = 66.67
if  actual == expected:
  print("Test passed")
else:
  print("Test failed, expected", expected, "but got", actual)

# Test 3 - Test with (20, None) and expect the answer to be -999999
print("Test 3 - Test with (20, None) and expect the answer to be -999999")
# ADD YOUR TEST CODE HERE #
actual = divide_nums(20, None)
expected = -999999
if  actual == expected:
  print("Test passed")
else:
  print("Test failed, expected", expected, "but got", actual)<jupyter_output>Test 1 - works with valid numbers (15 divided by 3 is 5)
Test passed
Test 2 - works with valid numbers (20, 0.3) and expect the answer to be 66.67
Test passed
Test 3 - Test with (20, None) and expect the answer to be -999999
Test passed<jupyter_text>Exercise 9 - function with `try..except` and test---Write a function called **find_list_average(numlist, listname)** that will accept a list of numbers `(numlist)` and a word representing the name of the list `(listname)`.  The function will calculate the average of `numlist` and will return the string "The average of the numbers in " + `listname` + " is " + `str(average)` + "."Use `try..except` to catch any runtime errors that might occur and return `"There was a problem"` if any do.Write a set of tests for this function to test that:  *  a valid set of numbers, with a valid name works*  it catches the error if the parameters are round the wrong way `(listname, numlist)` rather than `(numlist, listname)`*  it catches the error is the list is empty (which might result in dividing by 0)*  it catches the error if the numlist contains null values (None)*  it shows the result `The average of the numbers in unnamed list is 4.5.` if the listname is an empty string.  Note:  this test may well fail unless you had code in your function to deal with the string being empty.  This is a logic error - change the code so that if the listname is "" it will be set to "unnamed list".  Then run the tests again, they should all pass.<jupyter_code>def find_list_average(numlist, listname):
  try:
    if listname == '':
      listname = 'unnamed list'
    average = sum(numlist) / len(numlist)
    result_str = "The average of the numbers in " + listname + " is " + str(average) + "."
  except:
    if (type(numlist) == list) and (type(listname) == str):
      for x in numlist:
        if x is None:
          check_error(41)
          break
        if not str(x).isdigit():
          check_error(12)
          break
    if (type(numlist) == str) and (type(listname) == list):
      check_error(21)
    elif type(listname) != str:
      check_error(11)
    if len(numlist) == 0:
      check_error(31)
    return None, "There was a problem"
  return average, result_str

def check_error(error):
  if error == 11 or error == 12:
    # Test 1 - works with a valid set of numbers, with a valid name
    print("Test 1 - works with a valid set of numbers, with a valid name")
    return
  elif error == 21:
    # Test 2 - works only with parameters right way round
    print("Test 2 - works only with parameters right way round.")
  elif error == 31:
    # Test 3 - works only if the list was not empty
    print("Test 3 - works only if the list was not empty.")
  elif error == 41:
    # Test 4 - works only if the numlist does not contain null values (None)
    print("Test 4 - works only if the numlist does not contain null values (None).")
  return 

# Test 1 - works with a valid set of numbers, with a valid name
nums = [75, 67, 80]
list_name = 'Exam Scores'
average = sum(nums)/len(nums)

avg, actual = find_list_average(nums, list_name)
expected = f"The average of the numbers in {list_name} is {average}."
if  actual == expected:
  print("Test 1 - passed")
else:
  print(f"Test failed, expected '{expected}' \nbut got '{avg}, {actual}'")

print('')
# Test 2 - works only with parameters right way round
#nums = 'Exam Scores'
#list_name = [75, 67, 80]

avg, actual = find_list_average(nums, list_name)
expected = f"The average of the numbers in {list_name} is {average}."
if  actual == expected:
  print("Test 2 - passed")
else:
  print(f"Test failed, expected '{expected}' \nbut got '{avg}, {actual}'")

print('')
# Test 3 - works only if the list was not empty
#nums = []
list_name = 'Exam Scores'
avg, actual = find_list_average(nums, list_name)
expected = f"The average of the numbers in {list_name} is {average}."
if  actual == expected:
  print("Test 3 - passed")
else:
  print(f"Test failed, expected '{expected}' \nbut got '{avg}, {actual}'")

print('')
# Test 4 - works only if the numlist does not contain null values (None)
#nums = [75, 67, None, 80]
list_name = 'Exam Scores'
avg, actual = find_list_average(nums, list_name)
expected = f"The average of the numbers in {list_name} is {average}."
if  actual == expected:
  print("Test 4 - passed")
else:
  print(f"Test failed, expected '{expected}' \nbut got '{avg}, {actual}'")

print('')
# Test 5 - works even when the list_name is empty
nums = [75, 67, 80]
list_name = ''
find_list_average(nums, list_name)
avg, actual = find_list_average(nums, list_name)
expected = f"The average of the numbers in unnamed list is {average}."
if  actual == expected:
  print("Test 5 - passed")
else:
  print(f"Test failed, expected '{expected}' \nbut got '{avg}, {actual}'")<jupyter_output>Test 1 - passed

Test 2 - passed

Test 3 - passed

Test 4 - passed

Test 5 - passed
{'fraction_non_alphanumeric': 0.08526173232055585, 'fraction_numerical': 0.018547959724430314, 'mean_word_length': 3.9530475357247012, 'pattern_counts': {'":': 0, '<': 28, '<?xml version=': 0, '>': 28, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '340317', 'license': 'MIT', 'n_tokens_mistral': 5077, 'n_tokens_neox': 4752, 'n_words': 2534, 'path': 'Worksheets/13_Error_handling_and_testing.ipynb', 'repo_name': 'Imran-Github22/Programming-For-Data', 'size': '16983'}