From b5f12d7068d193e9cb951c763e16f3ea15f3f610 Mon Sep 17 00:00:00 2001 From: Ramon Figueiredo Date: Wed, 17 Jun 2026 21:53:08 -0700 Subject: [PATCH 1/2] [cuegui] Fix blank Redirect page when default show is missing Issue: - The CueCommander Redirect page rendered blank on setups without a show named "pipe" (the hardcoded default). - RedirectControls.__init__ called opencue.api.findShow(os.getenv("SHOW", "pipe")), which raised EntityNotFoundException when that show did not exist. - The plugin loader (Plugins.launchPlugin) swallows construction errors, so the dock was added but its content never built, leaving an empty page. Summary of changes: - Add RedirectControls.__getDefaultShow(), which falls back to the first active show when the configured/default show is not found, and returns None (logging a warning) when no active shows exist - Guard the None/empty-show paths in ShowCombo selection, showChanged, and GroupFilter.__populate_menu so the widget always builds - Add regression tests covering the missing-default-show and no-active-shows cases --- cuegui/cuegui/Redirect.py | 42 +++++++++++++++++++++++++++++++---- cuegui/tests/test_redirect.py | 29 ++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/cuegui/cuegui/Redirect.py b/cuegui/cuegui/Redirect.py index b7e60fcb5..a682ae085 100644 --- a/cuegui/cuegui/Redirect.py +++ b/cuegui/cuegui/Redirect.py @@ -189,6 +189,8 @@ def showChanged(self, show): def __populate_menu(self): self.__menu.clear() + if not self.__show: + return for group in self.__show.getGroups(): if opencue.id(group) in self.__actions: self.__menu.addAction(self.__actions[opencue.id(group)]) @@ -212,9 +214,10 @@ class RedirectControls(QtWidgets.QWidget): """ def __init__(self, parent=None): QtWidgets.QWidget.__init__(self, parent) - self.__current_show = opencue.api.findShow(os.getenv("SHOW", "pipe")) + self.__current_show = self.__getDefaultShow() - self.__show_combo = ShowCombo(self.__current_show.data.name, self) + show_name = self.__current_show.data.name if self.__current_show else "" + self.__show_combo = ShowCombo(show_name, self) self.__job_box = JobBox(self) self.__alloc_filter = AllocFilter(self) @@ -299,6 +302,31 @@ def __init__(self, parent=None): self.__show_combo.currentIndexChanged.connect(self.showChanged) # pylint: enable=no-member + @staticmethod + def __getDefaultShow(): + """Returns the show to select by default. + + Tries the show named by the SHOW environment variable (falling back to + "pipe"), and if that show does not exist, falls back to the first active + show alphabetically. Returns None when there are no active shows, so the + widget can still build instead of leaving a blank page. + + @return: The default show, or None if no active shows exist + @rtype: L{opencue.wrappers.show.Show} or None + """ + show_name = os.getenv("SHOW", "pipe") + try: + return opencue.api.findShow(show_name) + except opencue.exception.CueException: + logger.warning( + "Default show '%s' not found, falling back to first active show", show_name) + shows = opencue.api.getActiveShows() + if shows: + shows.sort(key=lambda x: x.data.name) + return shows[0] + logger.warning("No active shows found") + return None + def _cfg(self): ''' Loads (if necessary) and returns the config values. @@ -314,8 +342,14 @@ def _cfg(self): def showChanged(self, show_index): """Load a new show.""" del show_index - show = self.__show_combo.currentText() - self.__current_show = opencue.api.findShow(str(show)) + show = str(self.__show_combo.currentText()) + if not show: + return + try: + self.__current_show = opencue.api.findShow(show) + except opencue.exception.CueException: + logger.warning("Unable to load show '%s'", show) + return self.__include_group_btn.showChanged(self.__current_show) def detect(self, name=None): diff --git a/cuegui/tests/test_redirect.py b/cuegui/tests/test_redirect.py index 9d5e18e02..c90414acf 100644 --- a/cuegui/tests/test_redirect.py +++ b/cuegui/tests/test_redirect.py @@ -22,6 +22,8 @@ import qtpy.QtCore import qtpy.QtGui +import opencue.exception +import opencue.wrappers.show import opencue_proto.show_pb2 import cuegui.Redirect @@ -50,3 +52,30 @@ def setUp(self, getStubMock): def test_setup(self): pass + + @mock.patch('opencue.api.getJobNames', new=mock.Mock(return_value=[])) + @mock.patch('opencue.api.getAllocations', new=mock.Mock(return_value=[])) + @mock.patch('opencue.api.getActiveShows') + @mock.patch('opencue.api.findShow') + def test_builds_when_default_show_missing(self, findShowMock, getActiveShowsMock): + # The hardcoded default show ("pipe") does not exist on this cuebot. + findShowMock.side_effect = opencue.exception.EntityNotFoundException('show not found') + fallback_show = opencue.wrappers.show.Show( + opencue_proto.show_pb2.Show(name='fallback')) + getActiveShowsMock.return_value = [fallback_show] + + # Should build the widget instead of raising (which leaves a blank page). + widget = cuegui.Redirect.RedirectWidget() + self.assertIsNotNone(widget) + + @mock.patch('opencue.api.getJobNames', new=mock.Mock(return_value=[])) + @mock.patch('opencue.api.getAllocations', new=mock.Mock(return_value=[])) + @mock.patch('opencue.api.getActiveShows') + @mock.patch('opencue.api.findShow') + def test_builds_when_no_active_shows(self, findShowMock, getActiveShowsMock): + # No show matches the default and there are no active shows at all. + findShowMock.side_effect = opencue.exception.EntityNotFoundException('show not found') + getActiveShowsMock.return_value = [] + + widget = cuegui.Redirect.RedirectWidget() + self.assertIsNotNone(widget) From e576b53a75c82e5e44e75223f08b37b1f5792306 Mon Sep 17 00:00:00 2001 From: Ramon Figueiredo Date: Thu, 18 Jun 2026 14:00:07 -0700 Subject: [PATCH 2/2] [cuegui] Guard Redirect search against missing default show - Move getShow() to top of RedirectWidget.update() - Warn and return when no active show is available - Prevents AttributeError on show.data.name when getShow() is None --- cuegui/cuegui/Redirect.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cuegui/cuegui/Redirect.py b/cuegui/cuegui/Redirect.py index a682ae085..2da6dcfb0 100644 --- a/cuegui/cuegui/Redirect.py +++ b/cuegui/cuegui/Redirect.py @@ -785,6 +785,11 @@ def clearTarget(self): def update(self): """ Update the model """ + show = self.__controls.getShow() + if not show: + self.__warn("No active show is available to search.") + return + self.__model.clear() self.__model.setHorizontalHeaderLabels(RedirectWidget.HEADERS) @@ -795,7 +800,6 @@ def update(self): groupFilter = self.__controls.getIncludedGroups() jobRegexFilter = self.__controls.getJobNameExcludeRegex() - show = self.__controls.getShow() alloc = self.__controls.getAllocFilter() procs = opencue.api.getProcs(show=[str(show.data.name)], alloc=alloc.getSelected())