Python function doesn't return list[int] – solved

Question:

I’m still a beginner with just a few months experience in programming. I’m writing a program to roll dice in a few modes. I’m having a problem with my Selective mode.

Code:

from os import system
from platform import system as operating_system
from random import randint, randrange
from textwrap import dedent
from time import sleep
from typing import Union


def user_input() -> tuple[list[int], list[int]]:
    """Option selection"""
    dictionary_input: dict[str, str] = {
        "dice":
            "How many dice to roll?n",
        "sides":
            "Type in the maximum number:n",
        "confirm": """
            Your mode is {2}, you're rolling {0}d{1} dice.n
                Is this correct? (y/n/quit)n""",
        "includes_confirm": """
            Your mode is {2}, you're rolling {0} dice from numbers:
            {1}n
                Is this correct? (y/n/quit)n""",
        "mode": """
        Please select a number of your desired mode:
                1) Normal
                2) Increments of 10 (0 included)
                3) Selective (Only numbers specified by user)
                4) Exclude (Do not repeat numbers)
                5) Exit the appn"""}

    mode_input: dict[int, str] = {
        1: "Normal",
        2: "Increments",
        3: "Selective",
        4: "Exclude"
    }

    choice: list[int] = [0, 0, 0]
    dice, sides, mode = 0, 6, 0
    included_numbers: list[int] = []
    confirmation: str = ""
    while confirmation.upper() != "Y":
        try:
            # ask for mode
            mode = int(input(dedent(dictionary_input["mode"])))
            if mode == 5:
                break
            # ask for number of dice to roll
            dice = int(input(dictionary_input["dice"]))
            if mode == 3:
                # selective numbers options

                included_numbers.append(*number_inclusion()) # this part returns [], why?! (ノಠ益ಠ)ノ彡┻━┻
                print(included_numbers)                      # <- doesn't work
                if len(included_numbers) < 1:
                    continue
            else:
                # check for increment of 10 while in increments mode
                if mode == 2:
                    while sides % 10 != 0:
                        sides = int(input(dictionary_input["sides"]))
                        if sides % 10 != 0:
                            print(f"{sides} is not an increment of 10")
                            continue
                else:
                    sides = int(input(dictionary_input["sides"]))
            # confirmation from user
            if dice > 0 and sides > 0:
                if mode in mode_input:
                    if mode != 3:
                        confirmation = input(
                            dedent(dictionary_input["confirm"]).format(
                                dice, sides, mode_input[mode]))
                    else:
                        confirmation = input(
                            dedent(dictionary_input["includes_confirm"])
                            .format(dice, included_numbers,
                                    mode_input[mode]))
                    if confirmation.upper() != "QUIT":
                        choice[0], choice[1], choice[2] = dice, sides, mode
                    else:
                        break
                else:
                    print("Mode accepts numbers 1 - 5nn")
                    continue
            else:
                print("Only positive numbersnn")
                continue
        except ValueError:
            print("Only numbers acceptednn")
            continue
    return (choice, included_numbers)


def number_inclusion() -> list[int]:
    """Function used with mode Selective, stores user numbers"""
    included_numbers: list[int] = []
    temp_input: Union[int, str] = 0
    ask = {
        "first": """
            Type "review" to view numbers,
            type "remove" to delete numbers,
            type "done" to finish.""",
        "next":
            "Type in your number:n"
    }
    print(dedent(ask["first"]))
    while str(temp_input).upper() != "DONE":
        temp_input = input(dedent(ask["next"]))
        # input verification
        if temp_input.upper() not in ["DONE", "REVIEW", "REMOVE"]:
            try:
                if int(temp_input) < 0:
                    print("Only positive numbers accepted")
                    raise ValueError
                if float(temp_input) % 1 != 0:
                    print("Only integers accepted")
                    raise ValueError
                if int(temp_input) in included_numbers:
                    print("Number is already on the list")
                    continue
                included_numbers.append(int(temp_input))
                print(f"added number {temp_input} to selection")
                continue
            except ValueError:
                print("Unsuported option")
                continue
        if str(temp_input).upper() == "REVIEW":
            print(f"Saved numbers are:n{included_numbers}")
            continue
        if str(temp_input).upper() == "REMOVE":
            print(f"Saved numbers are:n{included_numbers}")
            try:
                temp_remove: int = int(input(
                    "Which would you like to remove?n"))
                if temp_remove in included_numbers:
                    included_numbers.remove(temp_remove)
                    print("Number removed")
                else:
                    raise ValueError
            except ValueError:
                print("Select a number from your list.nn")
        else:
            included_numbers.append(int(temp_input))
            print(f"added number {temp_input} to selection")
    print(included_numbers)                                      # <- doesn't work
    return included_numbers

The function number_inclusion() should return a list with numbers and append into included_numbers in user_input() with that list. While everything seems fine when reviewing

Type in your number:
>>>1
>>>2
>>>3
>>>review
Saved numbers are:
[1, 2, 3]

after the number_inclusion() execution in user_input() i get straight to ValueError with message Only numbers accepted from the first try…except block in user_input()

I want number_inclusion() to return a list[int] which will replace included_numbers in user_input()

Any tips how to improve this monster of a function would also be greatly appreciated!

EDIT:
Thanks to Paul Cornelius I’ve managed to fix my bugs. For anyone going through here this is what worked:

Removing this part at bottom of number_inclusion() since it’s already covered in the while loop

else:
    included_numbers.append(int(temp_input))
    print(f"added number {temp_input} to selection")

Replacing append with extend in user_input()

            if mode == 3:
                # selective numbers options
                included_numbers.extend(number_inclusion())
                if len(included_numbers) < 1:
                    continue

There’s probably more stuff to improve here but at least it works as intended.

Asked By: Zachiel

||

Answers:

There is a logic problem in your function number_inclusion, at the bottom of the loop:

    if str(temp_input).upper() == "REMOVE":
        print(f"Saved numbers are:n{included_numbers}")
        try:
            temp_remove: int = int(input(
                "Which would you like to remove?n"))
            if temp_remove in included_numbers:
                included_numbers.remove(temp_remove)
                print("Number removed")
            else:
                raise ValueError
        except ValueError:
            print("Select a number from your list.nn")
    else:
        included_numbers.append(int(temp_input))
        print(f"added number {temp_input} to selection")

If temp_input is ‘done’, the else: branch will be taken and int(temp_input) will raise a ValueError. You can simply eliminate the entire else block; the loop logic handles the case where temp_input is "done."

It is good practice to put as few lines of code between try: and except: as the program logic allows, as Tadhg McDonald-Jensen pointed out in his comment. Programs become hard to debug if there are too many lines of code where an Exception can possibly be raised before it gets handled.

You might also look at the traceback module, which allows you to print a traceback from inside an Exception handler block. Sometimes it quickly leads you to the problem. In fact, that’s how I found this one.

Answered By: Paul Cornelius