Okay, I might have a handle on this (maybe). What I think is happening, is:
First, the plugins clear their menu object. Then they recreate and add all the menu items they want to have. And call InterfaceAction.create_menu_action() to create the menu items. Which now calls self.gui.addAction(ac) on each action created to make it available .
So the next time the menu is rebuild, it's adding additional actions to self.gui that are overlapping.
Reading List has (and I lifted) some code to call self.gui.keyboard.unregister_shortcut(unique_name) after menu rebuild, but
only on menu items that have been 'removed', IE, not recreated. I honestly don't know why.
But replacing that with a brute force approach appears to work after limited testing: save the menu actions in a list and call unregister_shortcut and removeAction on all of them before rebuilding the menus.
Code:
for action in self.menu_actions:
self.gui.keyboard.unregister_shortcut(action.calibre_shortcut_unique_name)
self.gui.removeAction(action)
# rebuild menus...
self.gui.keyboard.finalize()
Are there any obvious reasons not to use this approach?