From 3a5d53fb662caa985424e9ce79b39b3df06cbf23 Mon Sep 17 00:00:00 2001 From: abrand Date: Fri, 21 Nov 2025 16:54:09 -0500 Subject: [PATCH 1/4] introduce CuebotConnectionManager --- pycue/opencue/cuebot.py | 450 ++++++++++++++++++++++++++++------------ 1 file changed, 317 insertions(+), 133 deletions(-) diff --git a/pycue/opencue/cuebot.py b/pycue/opencue/cuebot.py index 8d5c530d1..ca1999d7f 100644 --- a/pycue/opencue/cuebot.py +++ b/pycue/opencue/cuebot.py @@ -68,82 +68,132 @@ logger = logging.getLogger("opencue") -DEFAULT_MAX_MESSAGE_BYTES = 1024 ** 2 * 10 +DEFAULT_MAX_MESSAGE_BYTES = 1024**2 * 10 DEFAULT_GRPC_PORT = 8443 +_DEFAULT_TIMEOUT_MS = 10000 # 10 seconds -if platform.system() != 'Darwin': +if platform.system() != "Darwin": # Avoid spamming users with epoll fork warning messages os.environ["GRPC_POLL_STRATEGY"] = "epoll1" -class Cuebot(object): - """Used to manage the connection to the Cuebot. Normally the connection - to the Cuebot is made automatically as needed so you don't have to explicitly - call Cuebot.connect(). - - If you need to change the host(s) in which the library is connecting to, - you have a couple options. You can set it programmatically with - Cuebot.setHosts or set the CUEBOT_HOSTS environment variable - to a comma delimited list of host names.""" - RpcChannel = None - Hosts = [] - Stubs = {} - Config = opencue.config.load_config_from_file() - Timeout = Config.get('cuebot.timeout', 10000) + +class CuebotConnectionManager: + """Singleton manager for Cuebot connections. + + Manages the connection to the Cuebot server. Normally the connection + to the Cuebot is made automatically as needed so you don't have to explicitly + call init(). + + If you need to change the host(s) in which the library is connecting to, + you have a couple options. You can set it programmatically with + set_hosts or set the CUEBOT_HOSTS environment variable + to a comma delimited list of host names. + """ + + # Singleton instance + _instance = None + _initialized = False PROTO_MAP = { - 'action': filter_pb2, - 'allocation': facility_pb2, - 'comment': comment_pb2, - 'criterion': criterion_pb2, - 'cue': cue_pb2, - 'department': department_pb2, - 'depend': depend_pb2, - 'facility': facility_pb2, - 'filter': filter_pb2, - 'frame': job_pb2, - 'group': job_pb2, - 'host': host_pb2, - 'job': job_pb2, - 'layer': job_pb2, - 'limit': limit_pb2, - 'matcher': filter_pb2, - 'owner': host_pb2, - 'proc': host_pb2, - 'renderPartition': renderPartition_pb2, - 'service': service_pb2, - 'show': show_pb2, - 'subscription': subscription_pb2, - 'task': task_pb2 + "action": filter_pb2, + "allocation": facility_pb2, + "comment": comment_pb2, + "criterion": criterion_pb2, + "cue": cue_pb2, + "department": department_pb2, + "depend": depend_pb2, + "facility": facility_pb2, + "filter": filter_pb2, + "frame": job_pb2, + "group": job_pb2, + "host": host_pb2, + "job": job_pb2, + "layer": job_pb2, + "limit": limit_pb2, + "matcher": filter_pb2, + "owner": host_pb2, + "proc": host_pb2, + "renderPartition": renderPartition_pb2, + "service": service_pb2, + "show": show_pb2, + "subscription": subscription_pb2, + "task": task_pb2, } SERVICE_MAP = { - 'action': filter_pb2_grpc.ActionInterfaceStub, - 'allocation': facility_pb2_grpc.AllocationInterfaceStub, - 'comment': comment_pb2_grpc.CommentInterfaceStub, - 'cue': cue_pb2_grpc.CueInterfaceStub, - 'depend': depend_pb2_grpc.DependInterfaceStub, - 'department': department_pb2_grpc.DepartmentInterfaceStub, - 'facility': facility_pb2_grpc.FacilityInterfaceStub, - 'filter': filter_pb2_grpc.FilterInterfaceStub, - 'frame': job_pb2_grpc.FrameInterfaceStub, - 'group': job_pb2_grpc.GroupInterfaceStub, - 'host': host_pb2_grpc.HostInterfaceStub, - 'job': job_pb2_grpc.JobInterfaceStub, - 'layer': job_pb2_grpc.LayerInterfaceStub, - 'limit': limit_pb2_grpc.LimitInterfaceStub, - 'matcher': filter_pb2_grpc.MatcherInterfaceStub, - 'owner': host_pb2_grpc.OwnerInterfaceStub, - 'proc': host_pb2_grpc.ProcInterfaceStub, - 'renderPartition': renderPartition_pb2_grpc.RenderPartitionInterfaceStub, - 'service': service_pb2_grpc.ServiceInterfaceStub, - 'serviceOverride': service_pb2_grpc.ServiceOverrideInterfaceStub, - 'show': show_pb2_grpc.ShowInterfaceStub, - 'subscription': subscription_pb2_grpc.SubscriptionInterfaceStub, - 'task': task_pb2_grpc.TaskInterfaceStub + "action": filter_pb2_grpc.ActionInterfaceStub, + "allocation": facility_pb2_grpc.AllocationInterfaceStub, + "comment": comment_pb2_grpc.CommentInterfaceStub, + "cue": cue_pb2_grpc.CueInterfaceStub, + "depend": depend_pb2_grpc.DependInterfaceStub, + "department": department_pb2_grpc.DepartmentInterfaceStub, + "facility": facility_pb2_grpc.FacilityInterfaceStub, + "filter": filter_pb2_grpc.FilterInterfaceStub, + "frame": job_pb2_grpc.FrameInterfaceStub, + "group": job_pb2_grpc.GroupInterfaceStub, + "host": host_pb2_grpc.HostInterfaceStub, + "job": job_pb2_grpc.JobInterfaceStub, + "layer": job_pb2_grpc.LayerInterfaceStub, + "limit": limit_pb2_grpc.LimitInterfaceStub, + "matcher": filter_pb2_grpc.MatcherInterfaceStub, + "owner": host_pb2_grpc.OwnerInterfaceStub, + "proc": host_pb2_grpc.ProcInterfaceStub, + "renderPartition": renderPartition_pb2_grpc.RenderPartitionInterfaceStub, + "service": service_pb2_grpc.ServiceInterfaceStub, + "serviceOverride": service_pb2_grpc.ServiceOverrideInterfaceStub, + "show": show_pb2_grpc.ShowInterfaceStub, + "subscription": subscription_pb2_grpc.SubscriptionInterfaceStub, + "task": task_pb2_grpc.TaskInterfaceStub, } - @staticmethod - def init(config=None): + def __new__(cls): + """Singleton pattern implementation.""" + if cls._instance is None: + cls._instance = super().__new__(cls) + return cls._instance + + def __init__(self, config=None): + """Initialize the CuebotConnectionManager instance.""" + if self._initialized: + return + + self.rpc_channel = None + self.hosts = [] + self.stubs = {} + self._config = None + self._timeout = None + + self.initialize(config) + + # Mark as initialized to prevent re-initialization + self.__class__._initialized = True + + # Lazy property getters to prevent premature loading of configuration + @property + def config(self): + """Lazy getter for config.""" + if self._config is None: + self._config = opencue.config.load_config_from_file() + return self._config + + @config.setter + def config(self, value): + """Setter for config.""" + self._config = value + + @property + def timeout(self): + """Lazy getter for timeout.""" + if self._timeout is None: + self._timeout = self._config.get("cuebot.timeout", _DEFAULT_TIMEOUT_MS) + return self._timeout + + @timeout.setter + def timeout(self, value): + """Setter for timeout.""" + self._timeout = value + + def initialize(self, config=None): """Main init method for setting up the Cuebot object. Sets the communication channel and hosts. @@ -153,97 +203,109 @@ def init(config=None): hosts_env = os.getenv("CUEBOT_HOSTS") if config: - Cuebot.Config = config - Cuebot.Timeout = config.get('cuebot.timeout', Cuebot.Timeout) + self.config = config + self.timeout = config.get("cuebot.timeout", self.timeout) if hosts_env: - Cuebot.setHosts(hosts_env.split(",")) + self.set_hosts(hosts_env.split(",")) else: - facility = os.getenv("CUEBOT_FACILITY", Cuebot.Config.get("cuebot.facility_default")) - Cuebot.setHostWithFacility(facility) - if Cuebot.Hosts is None: - raise CueException('Cuebot host not set. Please ensure CUEBOT_HOSTS is set ' + - 'or a facility_default host is set in the yaml pycue config.') - - @staticmethod - def setChannel(): + facility = os.getenv( + "CUEBOT_FACILITY", self.config.get("cuebot.facility_default") + ) + self.set_host_with_facility(facility) + if not self.hosts: + raise CueException( + "Cuebot host not set. Please ensure CUEBOT_HOSTS is set " + + "or a facility_default host is set in the yaml pycue config." + ) + + def set_channel(self): """Sets the gRPC channel connection""" # gRPC must specify a single host. Randomize host list to balance load across cuebots. - hosts = list(Cuebot.Hosts) + hosts = list(self.hosts) shuffle(hosts) - maxMessageBytes = Cuebot.Config.get('cuebot.max_message_bytes', DEFAULT_MAX_MESSAGE_BYTES) + max_message_bytes = self.config.get( + "cuebot.max_message_bytes", DEFAULT_MAX_MESSAGE_BYTES + ) # create interceptors interceptors = ( RetryOnRpcErrorClientInterceptor( max_attempts=4, - sleeping_policy=ExponentialBackoff(init_backoff_ms=100, - max_backoff_ms=1600, - multiplier=2), + sleeping_policy=ExponentialBackoff( + init_backoff_ms=100, max_backoff_ms=1600, multiplier=2 + ), status_for_retry=(grpc.StatusCode.UNAVAILABLE,), ), ) - connectStr = "Not Defined" + connect_str = "Not Defined" for host in hosts: - if ':' in host: - connectStr = host + if ":" in host: + connect_str = host else: - connectStr = '%s:%s' % ( - host, Cuebot.Config.get('cuebot.grpc_port', DEFAULT_GRPC_PORT)) - # pylint: disable=logging-not-lazy - logger.debug('connecting to gRPC at %s' % connectStr) - # pylint: enable=logging-not-lazy + connect_str = "%s:%s" % ( + host, + self.config.get("cuebot.grpc_port", DEFAULT_GRPC_PORT), + ) + logger.debug("connecting to gRPC at %s", connect_str) # TODO(bcipriano) Configure gRPC TLS. (Issue #150) try: - Cuebot.RpcChannel = grpc.intercept_channel( - grpc.insecure_channel(connectStr, options=[ - ('grpc.max_send_message_length', maxMessageBytes), - ('grpc.max_receive_message_length', maxMessageBytes)]), - *interceptors) + self.rpc_channel = grpc.intercept_channel( + grpc.insecure_channel( + connect_str, + options=[ + ("grpc.max_send_message_length", max_message_bytes), + ("grpc.max_receive_message_length", max_message_bytes), + ], + ), + *interceptors, + ) # Test the connection - Cuebot.getStub('cue').GetSystemStats( - cue_pb2.CueGetSystemStatsRequest(), timeout=Cuebot.Timeout) + self.get_stub("cue").GetSystemStats( + cue_pb2.CueGetSystemStatsRequest(), timeout=self.timeout + ) # pylint: disable=broad-except except Exception: - logger.warning('Could not establish grpc channel with %s', connectStr) + logger.warning("Could not establish grpc channel with %s", connect_str) continue - atexit.register(Cuebot.closeChannel) + atexit.register(self.close_channel) return None - raise ConnectionException('No grpc connection could be established. ' + - 'Please check configured cuebot hosts: ' + connectStr) + raise ConnectionException( + "No grpc connection could be established. " + + "Please check configured cuebot hosts: " + + connect_str + ) - @staticmethod - def closeChannel(): + def close_channel(self): """Close the gRPC channel, delete it and reset it to None.""" - if Cuebot and Cuebot.RpcChannel is not None: - Cuebot.RpcChannel.close() - del Cuebot.RpcChannel - Cuebot.RpcChannel = None + if self.rpc_channel is not None: + self.rpc_channel.close() + del self.rpc_channel + self.rpc_channel = None - @staticmethod - def resetChannel(): + def reset_channel(self): """Close and reopen the gRPC channel.""" - Cuebot.closeChannel() - Cuebot.setChannel() + self.close_channel() + self.set_channel() - @staticmethod - def setHostWithFacility(facility): + def set_host_with_facility(self, facility): """Sets hosts to connect to based on the provided facility. If an unknown facility is provided, it will fall back to the one listed in cuebot.facility_default :type facility: str :param facility: a facility named in the config file""" - if facility not in list(Cuebot.Config.get("cuebot.facility").keys()): - default = Cuebot.Config.get("cuebot.facility_default") - logger.warning("The facility '%s' does not exist, defaulting to %s", facility, default) + if facility not in list(self.config.get("cuebot.facility").keys()): + default = self.config.get("cuebot.facility_default") + logger.warning( + "The facility '%s' does not exist, defaulting to %s", facility, default + ) facility = default logger.debug("setting facility to: %s", facility) - hosts = Cuebot.Config.get("cuebot.facility")[facility] - Cuebot.setHosts(hosts) + hosts = self.config.get("cuebot.facility")[facility] + self.set_hosts(hosts) - @staticmethod - def setHosts(hosts): + def set_hosts(self, hosts): """Sets the cuebot host names to connect to. :param hosts: a list of hosts or a host @@ -251,21 +313,20 @@ def setHosts(hosts): if isinstance(hosts, str): hosts = [hosts] logger.debug("setting new server hosts to: %s", hosts) - Cuebot.Hosts = hosts - Cuebot.resetChannel() + self.hosts = hosts + self.reset_channel() - @staticmethod - def setTimeout(timeout): + def set_timeout(self, timeout): """Sets the default network timeout. :param timeout: The network connection timeout in millis. :type timeout: int """ logger.debug("setting new server timeout to: %d", timeout) - Cuebot.Timeout = timeout + self.timeout = timeout @classmethod - def getProto(cls, name): + def get_proto(cls, name): """Returns a proto class for the given name.""" proto = cls.PROTO_MAP.get(name) if proto is None: @@ -273,30 +334,149 @@ def getProto(cls, name): return proto @classmethod - def getService(cls, name): + def get_service(cls, name): """Returns the service for the given name.""" service = cls.SERVICE_MAP.get(name) if service is None: raise ValueError("Could not find stub interface for {}.".format(name)) return service - @classmethod - def getStub(cls, name): + def get_stub(self, name): """Get the matching stub from the SERVICE_MAP. Reuse an existing one if possible. :param name: name of stub key for SERVICE_MAP :type name: str""" - if Cuebot.RpcChannel is None: - cls.init() + if self.rpc_channel is None: + self.initialize() + + service = self.get_service(name) + return service(self.rpc_channel) - service = cls.getService(name) - return service(Cuebot.RpcChannel) + +# Cuebot backward compatibility class +class _CuebotAdapter(CuebotConnectionManager): + """Backward compatibility class for Cuebot Connection Manager. + + Remap historical static methods and attributes to singleton instance methods. + """ + + # Backward compatibility static methods + @staticmethod + def init(config=None): + """Legacy static init method for backward compatibility.""" + instance = CuebotConnectionManager() + return instance.initialize(config) + + @staticmethod + def setChannel(): + """Legacy static method for backward compatibility.""" + instance = CuebotConnectionManager() + return instance.set_channel() + + @staticmethod + def setTimeout(timeout): + """Legacy static method for backward compatibility.""" + instance = CuebotConnectionManager() + return instance.set_timeout(timeout) + + @staticmethod + def setHostWithFacility(facility): + """Legacy static method for backward compatibility.""" + instance = CuebotConnectionManager() + return instance.set_host_with_facility(facility) + + @staticmethod + def setHosts(hosts): + """Legacy static method for backward compatibility.""" + instance = CuebotConnectionManager() + return instance.set_hosts(hosts) + + @staticmethod + def getStub(name: str): + """Legacy static method for backward compatibility.""" + instance = CuebotConnectionManager() + return instance.get_stub(name) + + @staticmethod + def closeChannel(): + """Legacy static method for backward compatibility.""" + instance = CuebotConnectionManager() + return instance.close_channel() + + @staticmethod + def resetChannel(): + """Legacy static method for backward compatibility.""" + instance = CuebotConnectionManager() + return instance.reset_channel() @staticmethod def getConfig(): - """Gets the Cuebot config object, originally read in from the config file on disk.""" - return Cuebot.Config + """Legacy instance method for backward compatibility.""" + instance = CuebotConnectionManager() + return instance.config + + # Direct classmethod mappings + getService = CuebotConnectionManager.get_service + getProto = CuebotConnectionManager.get_proto + + # Backward compatibility properties for old attribute access + @property + def RpcChannel(self): + """Backward compatibility property for RpcChannel.""" + return self.rpc_channel + + @RpcChannel.setter + def RpcChannel(self, value): + """Backward compatibility setter for RpcChannel.""" + self.rpc_channel = value + + @property + def Hosts(self): + """Backward compatibility property for Hosts.""" + return self.hosts + + @Hosts.setter + def Hosts(self, value): + """Backward compatibility setter for Hosts.""" + self.hosts = value + + @property + def Stubs(self): + """Backward compatibility property for Stubs.""" + return self.stubs + + @Stubs.setter + def Stubs(self, value): + """Backward compatibility setter for Stubs.""" + self.stubs = value + + @property + def Config(self): + """Backward compatibility property for Config.""" + return self.config + + @Config.setter + def Config(self, value): + """Backward compatibility setter for Config.""" + self.config = value + + @property + def Timeout(self): + """Backward compatibility property for Timeout.""" + return self.timeout + + @Timeout.setter + def Timeout(self, value): + """Backward compatibility setter for Timeout.""" + self.timeout = value + + def __call__(self, *args, **kwargs): + """Noop to support old `Cuebot()` calls. + + Returns itself. + """ + return self # Python 2/3 compatible implementation of ABC @@ -389,3 +569,7 @@ def intercept_stream_unary( ): return self._intercept_call(continuation, client_call_details, request_iterator) + + +# Backward compatibility namespace as a singleton +Cuebot = _CuebotAdapter() From e220f1ddbcb5bb94f6b0e5ad2e68910e33b4b36d Mon Sep 17 00:00:00 2001 From: abrand Date: Fri, 21 Nov 2025 18:08:43 -0500 Subject: [PATCH 2/4] failing proto: Failed to launch and add job: Failed to parse job spec XML, java.net.MalformedURLException --- pycue/opencue/cuebot.py | 122 ++++++++++++++++++---------------------- 1 file changed, 54 insertions(+), 68 deletions(-) diff --git a/pycue/opencue/cuebot.py b/pycue/opencue/cuebot.py index ca1999d7f..59b9a966e 100644 --- a/pycue/opencue/cuebot.py +++ b/pycue/opencue/cuebot.py @@ -154,45 +154,21 @@ def __new__(cls): def __init__(self, config=None): """Initialize the CuebotConnectionManager instance.""" + print("CONSTRUCTOR INIT") if self._initialized: return self.rpc_channel = None self.hosts = [] self.stubs = {} - self._config = None - self._timeout = None + self.config = opencue.config.load_config_from_file() + self.timeout = self.config.get("cuebot.timeout", _DEFAULT_TIMEOUT_MS) self.initialize(config) # Mark as initialized to prevent re-initialization self.__class__._initialized = True - # Lazy property getters to prevent premature loading of configuration - @property - def config(self): - """Lazy getter for config.""" - if self._config is None: - self._config = opencue.config.load_config_from_file() - return self._config - - @config.setter - def config(self, value): - """Setter for config.""" - self._config = value - - @property - def timeout(self): - """Lazy getter for timeout.""" - if self._timeout is None: - self._timeout = self._config.get("cuebot.timeout", _DEFAULT_TIMEOUT_MS) - return self._timeout - - @timeout.setter - def timeout(self, value): - """Setter for timeout.""" - self._timeout = value - def initialize(self, config=None): """Main init method for setting up the Cuebot object. Sets the communication channel and hosts. @@ -200,6 +176,7 @@ def initialize(self, config=None): :type config: dict :param config: config dictionary, this will override the config read from disk """ + print("Initializing CuebotConnectionManager...", config) hosts_env = os.getenv("CUEBOT_HOSTS") if config: @@ -220,6 +197,8 @@ def initialize(self, config=None): def set_channel(self): """Sets the gRPC channel connection""" + print("Setting gRPC channel...") + logger.debug("setting gRPC channel to hosts: %s", self.hosts) # gRPC must specify a single host. Randomize host list to balance load across cuebots. hosts = list(self.hosts) shuffle(hosts) @@ -247,7 +226,7 @@ def set_channel(self): host, self.config.get("cuebot.grpc_port", DEFAULT_GRPC_PORT), ) - logger.debug("connecting to gRPC at %s", connect_str) + logger.debug("connecting to gRPC at %s" % connect_str) # TODO(bcipriano) Configure gRPC TLS. (Issue #150) try: self.rpc_channel = grpc.intercept_channel( @@ -260,6 +239,7 @@ def set_channel(self): ), *interceptors, ) + print("GRPC channel set to:", self.rpc_channel) # Test the connection self.get_stub("cue").GetSystemStats( cue_pb2.CueGetSystemStatsRequest(), timeout=self.timeout @@ -285,6 +265,7 @@ def close_channel(self): def reset_channel(self): """Close and reopen the gRPC channel.""" + print("Resetting gRPC channel...") self.close_channel() self.set_channel() @@ -310,6 +291,7 @@ def set_hosts(self, hosts): :param hosts: a list of hosts or a host :type hosts: list or str""" + print("Setting Cuebot hosts to:", hosts) if isinstance(hosts, str): hosts = [hosts] logger.debug("setting new server hosts to: %s", hosts) @@ -347,6 +329,8 @@ def get_stub(self, name): :param name: name of stub key for SERVICE_MAP :type name: str""" + print("Getting stub for service:", name) + print("Current rpc_channel:", self.rpc_channel) if self.rpc_channel is None: self.initialize() @@ -354,122 +338,124 @@ def get_stub(self, name): return service(self.rpc_channel) -# Cuebot backward compatibility class -class _CuebotAdapter(CuebotConnectionManager): - """Backward compatibility class for Cuebot Connection Manager. +# Lazy proxy class for backward compatibility +class _CuebotLazyProxy: + """Lazy proxy for backward compatibility with the old Cuebot interface. - Remap historical static methods and attributes to singleton instance methods. + This class acts as a proxy that only instantiates the actual CuebotConnectionManager + when methods or properties are first accessed, avoiding premature initialization + during module import. """ + # Class attributes for backward compatibility + PROTO_MAP = CuebotConnectionManager.PROTO_MAP + SERVICE_MAP = CuebotConnectionManager.SERVICE_MAP - # Backward compatibility static methods + # Backward compatibility static methods with original signatures @staticmethod def init(config=None): """Legacy static init method for backward compatibility.""" - instance = CuebotConnectionManager() - return instance.initialize(config) + return CuebotConnectionManager().initialize(config) @staticmethod def setChannel(): """Legacy static method for backward compatibility.""" - instance = CuebotConnectionManager() - return instance.set_channel() + return CuebotConnectionManager().set_channel() @staticmethod def setTimeout(timeout): """Legacy static method for backward compatibility.""" - instance = CuebotConnectionManager() - return instance.set_timeout(timeout) + return CuebotConnectionManager().set_timeout(timeout) @staticmethod def setHostWithFacility(facility): """Legacy static method for backward compatibility.""" - instance = CuebotConnectionManager() - return instance.set_host_with_facility(facility) + return CuebotConnectionManager().set_host_with_facility(facility) @staticmethod def setHosts(hosts): """Legacy static method for backward compatibility.""" - instance = CuebotConnectionManager() - return instance.set_hosts(hosts) + return CuebotConnectionManager().set_hosts(hosts) @staticmethod - def getStub(name: str): + def getStub(name): """Legacy static method for backward compatibility.""" - instance = CuebotConnectionManager() - return instance.get_stub(name) + return CuebotConnectionManager().get_stub(name) @staticmethod def closeChannel(): """Legacy static method for backward compatibility.""" - instance = CuebotConnectionManager() - return instance.close_channel() + return CuebotConnectionManager().close_channel() @staticmethod def resetChannel(): """Legacy static method for backward compatibility.""" - instance = CuebotConnectionManager() - return instance.reset_channel() + return CuebotConnectionManager().reset_channel() @staticmethod def getConfig(): - """Legacy instance method for backward compatibility.""" - instance = CuebotConnectionManager() - return instance.config + """Legacy static method for backward compatibility.""" + return CuebotConnectionManager().config - # Direct classmethod mappings - getService = CuebotConnectionManager.get_service - getProto = CuebotConnectionManager.get_proto + @classmethod + def getService(cls, name): + """Legacy class method for backward compatibility.""" + return CuebotConnectionManager.get_service(name) + + @classmethod + def getProto(cls, name): + """Legacy class method for backward compatibility.""" + return CuebotConnectionManager.get_proto(name) # Backward compatibility properties for old attribute access @property def RpcChannel(self): """Backward compatibility property for RpcChannel.""" - return self.rpc_channel + return CuebotConnectionManager().rpc_channel @RpcChannel.setter def RpcChannel(self, value): """Backward compatibility setter for RpcChannel.""" - self.rpc_channel = value + CuebotConnectionManager().rpc_channel = value @property def Hosts(self): """Backward compatibility property for Hosts.""" - return self.hosts + return CuebotConnectionManager().hosts @Hosts.setter def Hosts(self, value): """Backward compatibility setter for Hosts.""" - self.hosts = value + CuebotConnectionManager().hosts = value @property def Stubs(self): """Backward compatibility property for Stubs.""" - return self.stubs + return CuebotConnectionManager().stubs @Stubs.setter def Stubs(self, value): """Backward compatibility setter for Stubs.""" - self.stubs = value + CuebotConnectionManager().stubs = value @property def Config(self): """Backward compatibility property for Config.""" - return self.config + return CuebotConnectionManager().config @Config.setter def Config(self, value): """Backward compatibility setter for Config.""" - self.config = value + CuebotConnectionManager().config = value @property def Timeout(self): """Backward compatibility property for Timeout.""" - return self.timeout + return CuebotConnectionManager().timeout @Timeout.setter def Timeout(self, value): """Backward compatibility setter for Timeout.""" - self.timeout = value + CuebotConnectionManager().timeout = value def __call__(self, *args, **kwargs): """Noop to support old `Cuebot()` calls. @@ -571,5 +557,5 @@ def intercept_stream_unary( request_iterator) -# Backward compatibility namespace as a singleton -Cuebot = _CuebotAdapter() +# Backward compatibility namespace as a lazy proxy +Cuebot = _CuebotLazyProxy() From 2d3d6f32ddb42d9eef41e12e756ef6a191f43863 Mon Sep 17 00:00:00 2001 From: abrand Date: Fri, 21 Nov 2025 19:05:58 -0500 Subject: [PATCH 3/4] remove debug print --- pycue/opencue/cuebot.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/pycue/opencue/cuebot.py b/pycue/opencue/cuebot.py index 59b9a966e..4880bfd0f 100644 --- a/pycue/opencue/cuebot.py +++ b/pycue/opencue/cuebot.py @@ -154,7 +154,6 @@ def __new__(cls): def __init__(self, config=None): """Initialize the CuebotConnectionManager instance.""" - print("CONSTRUCTOR INIT") if self._initialized: return @@ -176,7 +175,6 @@ def initialize(self, config=None): :type config: dict :param config: config dictionary, this will override the config read from disk """ - print("Initializing CuebotConnectionManager...", config) hosts_env = os.getenv("CUEBOT_HOSTS") if config: @@ -197,8 +195,6 @@ def initialize(self, config=None): def set_channel(self): """Sets the gRPC channel connection""" - print("Setting gRPC channel...") - logger.debug("setting gRPC channel to hosts: %s", self.hosts) # gRPC must specify a single host. Randomize host list to balance load across cuebots. hosts = list(self.hosts) shuffle(hosts) @@ -239,7 +235,6 @@ def set_channel(self): ), *interceptors, ) - print("GRPC channel set to:", self.rpc_channel) # Test the connection self.get_stub("cue").GetSystemStats( cue_pb2.CueGetSystemStatsRequest(), timeout=self.timeout @@ -265,7 +260,6 @@ def close_channel(self): def reset_channel(self): """Close and reopen the gRPC channel.""" - print("Resetting gRPC channel...") self.close_channel() self.set_channel() @@ -291,7 +285,6 @@ def set_hosts(self, hosts): :param hosts: a list of hosts or a host :type hosts: list or str""" - print("Setting Cuebot hosts to:", hosts) if isinstance(hosts, str): hosts = [hosts] logger.debug("setting new server hosts to: %s", hosts) @@ -329,8 +322,6 @@ def get_stub(self, name): :param name: name of stub key for SERVICE_MAP :type name: str""" - print("Getting stub for service:", name) - print("Current rpc_channel:", self.rpc_channel) if self.rpc_channel is None: self.initialize() From 140d712db29b040be28a739740277bd6f95fb700 Mon Sep 17 00:00:00 2001 From: abrand Date: Fri, 21 Nov 2025 19:06:57 -0500 Subject: [PATCH 4/4] remove py2 ABC compat --- pycue/opencue/cuebot.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pycue/opencue/cuebot.py b/pycue/opencue/cuebot.py index 4880bfd0f..d14fb5160 100644 --- a/pycue/opencue/cuebot.py +++ b/pycue/opencue/cuebot.py @@ -18,7 +18,6 @@ from __future__ import division from __future__ import absolute_import -from builtins import object from random import shuffle import abc import time @@ -456,11 +455,7 @@ def __call__(self, *args, **kwargs): return self -# Python 2/3 compatible implementation of ABC -ABC = abc.ABCMeta('ABC', (object,), {'__slots__': ()}) - - -class SleepingPolicy(ABC): +class SleepingPolicy(abc.ABC): """ Implement policy for sleeping between API retries """