You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

程序未返回上级菜单直接退出的问题排查

问题:按回车键后程序终止,未返回top_menu

执行以下步骤后,程序直接终止,未重新显示top_menu:

  • 选择客户端(输入2);
  • 从列表中选择客户端(输入2);
  • 按两次回车键。

复现代码

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 = []  # 存储菜单函数引用的栈

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 == "":
        # 在顶级菜单按回车直接退出
        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')
    pprint(menu_stack)
    if not clients:
        print("\nNo clients available. Please create one first.")
        sleep(2)
        if flag: print(5)
        return  # 返回上一级菜单        
    
    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 flag: print(6)
        pprint(menu_stack)
        traceback.print_stack()
        return  # 返回上一级菜单
    
    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  # 返回select_client菜单
    
    if flag: print(9)
    return

def print_menu(header, msg, items, header_only):
    
    # 获取终端宽度
    terminal_width = os.get_terminal_size().columns

    # 字段尺寸
    item_no_size = 10
    item_size = 40

    # 计算内容居中的起始位置
    center_padding = ((terminal_width - item_no_size - item_size) // 2 ) 

    while True:
        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) ) # 打印表头下划线        

        # 打印任务列表
        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  # 输入有效,退出循环
            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__":
    with open('clients.json', 'r') as json_file:
        clients = json.load(json_file)
    top_menu()
    print('program end')

问题原因

核心问题在于菜单栈的调用逻辑不统一,导致返回链断裂:

  1. 从action_menu按回车返回时,select_client执行完action_menu后直接return,未触发栈的弹出和上一级菜单调用;
  2. 从select_client按回车返回时,top_menu中执行menu_stack.pop()(),但此时栈状态不符合预期,调用链中断,程序直接退出。

修复方案

统一菜单栈的入栈和返回逻辑,确保每一级菜单的返回都能正确触发上一级菜单的显示:

修改后的关键函数

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:
        menu_stack.append(top_menu)
        select_client()
    elif choice == "":
        print("Exiting...")
        with open('clients.json', 'w') as json_file:
            json.dump(clients, json_file, indent=4) 
        return
    
    # 仅当栈不为空时,弹出并调用上一级菜单
    if menu_stack:
        menu_stack.pop()()

def select_client():
    global active_client
    print('appending')
    pprint(menu_stack)
    if not clients:
        print("\nNo clients available. Please create one first.")
        sleep(2)
        # 返回上一级菜单
        if menu_stack:
            menu_stack.pop()()
        return        
    
    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 == "":
        # 返回top_menu
        if menu_stack:
            menu_stack.pop()()
        return
    
    active_client = clients[choice-1]
    menu_stack.append(select_client)
    action_menu()

def action_menu():
    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 == "":             
        # 返回select_client
        if menu_stack:
            menu_stack.pop()()
        return

修改说明

  1. 统一入栈时机:进入子菜单前,将当前菜单函数入栈(如top_menu进入select_client时入栈top_menu,select_client进入action_menu时入栈select_client);
  2. 统一返回逻辑:当用户按回车(空输入)时,直接弹出栈顶并调用上一级菜单,确保返回链完整;
  3. 移除冗余逻辑:删除原代码中多余的return后栈操作,避免逻辑冲突;
  4. 栈安全判断:执行pop()前先检查栈是否为空,防止报错。

内容的提问来源于stack exchange,提问作者DCR

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 12:31:04