diff --git a/pype/modules/webserver/server.py b/pype/modules/webserver/server.py index 41f8f86a1b..e4b0ec236b 100644 --- a/pype/modules/webserver/server.py +++ b/pype/modules/webserver/server.py @@ -21,7 +21,11 @@ class WebServerManager: # add route with multiple methods for single "external app" - self.webserver_thread = WebServerThread(self, self.module.port) + self.webserver_thread = WebServerThread(self) + + @property + def port(self): + return self.module.port def add_route(self, *args, **kwargs): self.app.router.add_route(*args, **kwargs) @@ -60,17 +64,20 @@ class WebServerManager: class WebServerThread(threading.Thread): """ Listener for requests in thread.""" - def __init__(self, manager, port): + def __init__(self, manager): super(WebServerThread, self).__init__() self.is_running = False - self.port = port self.manager = manager self.loop = None self.runner = None self.site = None self.tasks = [] + @property + def port(self): + return self.manager.port + def run(self): self.is_running = True diff --git a/pype/modules/webserver/webserver_module.py b/pype/modules/webserver/webserver_module.py index 0cdf478d8c..5ebfdbe2d2 100644 --- a/pype/modules/webserver/webserver_module.py +++ b/pype/modules/webserver/webserver_module.py @@ -1,4 +1,5 @@ import os +import socket from pype import resources from .. import PypeModule, ITrayService @@ -11,8 +12,7 @@ class WebServerModule(PypeModule, ITrayService): self.enabled = True self.server_manager = None - # TODO find free port - self.port = 8098 + self.port = self.find_free_port() def connect_with_modules(self, *_a, **_kw): return @@ -53,3 +53,58 @@ class WebServerModule(PypeModule, ITrayService): self.server_manager.on_stop_callbacks.append( self.set_service_failed_icon ) + + @staticmethod + def find_free_port( + port_from=None, port_to=None, exclude_ports=None, host=None + ): + """Find available socket port from entered range. + + It is also possible to only check if entered port is available. + + Args: + port_from (int): Port number which is checked as first. + port_to (int): Last port that is checked in sequence from entered + `port_from`. Only `port_from` is checked if is not entered. + Nothing is processed if is equeal to `port_from`! + exclude_ports (list, tuple, set): List of ports that won't be + checked form entered range. + host (str): Host where will check for free ports. Set to + "localhost" by default. + """ + if port_from is None: + port_from = 8079 + + if port_to is None: + port_to = 65535 + + # Excluded ports (e.g. reserved for other servers/clients) + if exclude_ports is None: + exclude_ports = [] + + # Default host is localhost but it is possible to look for other hosts + if host is None: + host = "localhost" + + found_port = None + for port in range(port_from, port_to + 1): + if port in exclude_ports: + continue + + sock = None + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.bind((host, port)) + found_port = port + + except socket.error: + continue + + finally: + if sock: + sock.close() + + if found_port is not None: + break + + return found_port