trying to make this a minimal reproduction of the problem. If I run the following code as follows:
The program terminates instead of reprinting the top_menu.
here's the code:
import os
from time import sleep
from pprint import pprint
import json
import traceback
os.chdir(os.path.dirname(__file__))
active_client = {}
flag = True
menus = {
'top_menu': ['Create Client', 'Select Client'],
'select_menu':['dynamic'],
'action_menu':['Transactions', 'View Portfolio', 'View Transactions'],
'trans_menu':['Buy', 'Sell', 'Contribution', 'Withdrawal'],
'port_menu': ['Current Portfolio', 'Portfolio History']
}
menu_stack = [] # Stack to hold function references
def top_menu():
print('enter top')
pprint(menu_stack)
header ="Equity Portfolio Management System"
msg = "Select Task : "
tasks = menus['top_menu']
choice = print_menu(header, msg, tasks, False)
if choice == 2:
print('before')
menu_stack.append(top_menu)
print(menu_stack)
select_client()
print('after')
pprint(menu_stack)
elif choice == "":
# If return is pressed and we're at the top menu, exit directly
print("Exiting...")
with open('clients.json', 'w') as json_file:
json.dump(clients, json_file, indent=4)
if flag: print(1)
return
if flag: print(2)
pprint(menu_stack)
traceback.print_stack()
menu_stack.pop()()
return
def select_client():
global active_client
print('appending')
#menu_stack.append(top_menu)
pprint(menu_stack)
if not clients:
print("\nNo clients available. Please create one first.")
sleep(2)
# Pop the current menu and return to the previous menu
if flag: print(5)
return # Go back to the previous menu
names = []
for index, client in enumerate(clients, 1):
names.append(client['first name'] + ' ' + client['last name'])
header = 'Client Names'
msg = ' Select client : '
choice = print_menu(header, msg, names, False)
if choice == "":
# If return is pressed, go back to the top menu
if flag: print(6)
pprint(menu_stack)
traceback.print_stack()
return # Go back to the previous menu
active_client = clients[choice-1]
action_menu()
if flag: print(7)
pprint(menu_stack)
traceback.print_stack()
return
def action_menu():
menu_stack.append(select_client)
pprint(menu_stack)
header = 'Actions for ' + active_client['first name'] + ' ' + active_client['last name']
msg = 'Select Action : '
tasks = menus['action_menu']
choice = print_menu(header, msg, tasks, False)
if choice == "":
if flag: print(8)
pprint(menu_stack)
return # Go back to select_client if input is empty
if flag: print(9)
return
def print_menu(header, msg, items, header_only):
# Get the terminal width
terminal_width = os.get_terminal_size().columns
# Field sizes
item_no_size = 10
item_size = 40
# Calculate the starting position to center the content
center_padding = ((terminal_width - item_no_size - item_size) // 2 )
# Input validation loop
while True:
#clear_terminal()
print("\n" + "Babson Enterprises".center(terminal_width))
print(header.center(terminal_width, "*"))
print("*" * terminal_width + "\n")
if header_only: return
print(" " * center_padding + "Task No".ljust(item_no_size) + " Task".ljust(item_size))
print(" " * center_padding + "-" * (item_no_size + item_size) ) # Print a line underneath the headers
# Print tasks with extra spacing
for index, item_name in enumerate(items):
print(" " * center_padding + str(index+1).center(item_no_size) + item_name.ljust(item_size))
print("\n")
return int_input(" " * center_padding + msg, items)
def int_input(msg, items):
while True:
item_selected = input(msg)
if item_selected == '':
return ''
try:
item_selected = int(item_selected)
if 1 <= item_selected <= len(items):
return item_selected # Valid input, exit loop
else:
msg = "******Invalid choice. Please enter a number " + \
"between 1 and " + str(len(items)) + " : "
except ValueError:
msg = "Please enter a valid integer."
if __name__ == "__main__":
# Start at the top_menu and add it to the stack
with open('clients.json', 'r') as json_file:
clients = json.load(json_file)
top_menu()
print('program end')
#traceback.print_stack()
menu_stack.pop()()
At the end of your method always assumes there's something left to catch in the stack.
When you hit the last Enter
, and top_menu()
also returns, the stack is empty.
https://mimo.org/glossary/python/pop()
While the
pop()
method is highly useful, it's important to take care when you use the method. Usingpop()
on an empty list or with an out-of-range index results in anIndexError
(pop()
index out of range).
You are not able to see this exception (IndexError
) as the stack trace gets "devoured" due to top-level context ending.
Easiest way to fix this is to check if there's something in the stack:
if menu_stack:
menu_stack.pop()()
else:
top_menu()