예제 #1
0
    def restart(self):
        """ (re)starts a Python IDE-server
        this method is complicated by SublimePythonIDE having two different debug modes,
            - one in which the server is started manually by the developer, in which case this
            developer has to set the DEBUG_PORT constant
            - and one case where the server is started automatically but in a verbose mode,
            in which it prints to its stderr, which is copied to ST3's console by an
            AsynchronousFileReader. For this the developer has to set SERVER_DEBUGGING to True
        """
        try:
            if DEBUG_PORT is not None:
                # debug mode one
                self.port = DEBUG_PORT
                self.proc = DebugProcDummy()
                print("started server on user-defined FIXED port %i with %s" % (self.port, self.python))
            elif SERVER_DEBUGGING:
                # debug mode two
                self.port = self.get_free_port()
                proc_args = [self.python, SERVER_SCRIPT, str(self.port), " --debug"]
                self.proc = subprocess.Popen(
                    proc_args, cwd=os.path.dirname(self.python), stderr=subprocess.PIPE, creationflags=CREATION_FLAGS
                )
                self.queue = Queue()
                self.stderr_reader = AsynchronousFileReader(
                    "Server on port %i - STDERR" % self.port, self.proc.stderr, self.queue
                )
                self.stderr_reader.start()
                sublime.set_timeout_async(self.debug_consume, 1000)
                print("started server on port %i with %s IN DEBUG MODE" % (self.port, self.python))
            else:
                # standard run of the server in end-user mode
                self.port = self.get_free_port()
                proc_args = [self.python, SERVER_SCRIPT, str(self.port)]
                self.proc = subprocess.Popen(proc_args, cwd=os.path.dirname(self.python), creationflags=CREATION_FLAGS)
                print("started server on port %i with %s" % (self.port, self.python))

            # wait 100 ms to make sure python proc is still running
            for i in range(10):
                time.sleep(0.01)
                if self.proc.poll():
                    if SERVER_DEBUGGING:
                        print(sys.exc_info())
                    raise OSError(None, "Python interpretor crashed (using path %s)" % self.python)

            # in any case, we also need a local client object
            self.proxy = xmlrpc.client.ServerProxy(
                "http://%s:%i" % (self.resolve_localhost(), self.port), allow_none=True
            )
            self.set_heartbeat_timer()
        except OSError as e:
            print("error starting server:", e)
            print("-----------------------------------------------------------------------------------------------")
            print("Try to use an absolute path to your projects python interpreter. On Windows try to use forward")
            print("slashes as in C:/Python27/python.exe or properly escape with double-backslashes" "")
            print("-----------------------------------------------------------------------------------------------")
            raise e
class Proxy(object):
    '''Abstracts the external Python processes that do the actual
    work. SublimePython just calls local methods on Proxy objects.
    The Proxy objects start external Python processes, send them heartbeat
    messages, communicate with them and restart them if necessary.'''
    def __init__(self, python):
        self.python = python
        self.proc = None
        self.proxy = None
        self.port = None
        self.stderr_reader = None
        self.queue = None
        self.rpc_lock = threading.Lock()
        self.restart()

    def get_free_port(self):
        s = socket.socket()
        s.bind(('', 0))
        port = s.getsockname()[1]
        s.close()
        return port

    def resolve_localhost(self):
        return socket.gethostbyname("localhost")

    def restart(self):
        ''' (re)starts a Python IDE-server
        this method is complicated by SublimePythonIDE having two different debug modes,
            - one in which the server is started manually by the developer, in which case this
            developer has to set the DEBUG_PORT constant
            - and one case where the server is started automatically but in a verbose mode,
            in which it prints to its stderr, which is copied to ST3's console by an
            AsynchronousFileReader. For this the developer has to set SERVER_DEBUGGING to True
        '''
        try:
            if DEBUG_PORT is not None:
                # debug mode one
                self.port = DEBUG_PORT
                self.proc = DebugProcDummy()
                print("started server on user-defined FIXED port %i with %s" %
                      (self.port, self.python))
            elif SERVER_DEBUGGING:
                # debug mode two
                self.port = self.get_free_port()
                proc_args = [
                    self.python, SERVER_SCRIPT,
                    str(self.port), " --debug"
                ]
                self.proc = subprocess.Popen(proc_args,
                                             cwd=os.path.dirname(self.python),
                                             stderr=subprocess.PIPE,
                                             creationflags=CREATION_FLAGS)
                self.queue = Queue()
                self.stderr_reader = AsynchronousFileReader(
                    "Server on port %i - STDERR" % self.port, self.proc.stderr,
                    self.queue)
                self.stderr_reader.start()
                sublime.set_timeout_async(self.debug_consume, 1000)
                print("started server on port %i with %s IN DEBUG MODE" %
                      (self.port, self.python))
            else:
                # standard run of the server in end-user mode
                self.port = self.get_free_port()
                proc_args = [self.python, SERVER_SCRIPT, str(self.port)]
                self.proc = subprocess.Popen(proc_args,
                                             cwd=os.path.dirname(self.python),
                                             creationflags=CREATION_FLAGS)
                print("started server on port %i with %s" %
                      (self.port, self.python))

            # wait 100 ms to make sure python proc is still running
            for i in range(10):
                time.sleep(0.01)
                if self.proc.poll():
                    if SERVER_DEBUGGING:
                        print(sys.exc_info())
                    raise OSError(
                        None, "Python interpretor crashed (using path %s)" %
                        self.python)

            # in any case, we also need a local client object
            self.proxy = xmlrpc.client.ServerProxy(
                'http://%s:%i' % (self.resolve_localhost(), self.port),
                allow_none=True)
            self.set_heartbeat_timer()
        except OSError as e:
            print("error starting server:", e)
            print(
                "-----------------------------------------------------------------------------------------------"
            )
            print(
                "Try to use an absolute path to your projects python interpreter. On Windows try to use forward"
            )
            print(
                "slashes as in C:/Python27/python.exe or properly escape with double-backslashes"
                "")
            print(
                "-----------------------------------------------------------------------------------------------"
            )
            raise e

    def debug_consume(self):
        '''
        If SERVER_DEBUGGING is enabled, is called by ST every 1000ms and prints
        output from server debugging readers.
        '''
        # Check the queues if we received some output (until there is nothing more to get).
        while not self.queue.empty():
            line = self.queue.get()
            print(str(line))
        # Sleep a bit before asking the readers again.
        sublime.set_timeout_async(self.debug_consume, 1000)

    def set_heartbeat_timer(self):
        sublime.set_timeout_async(self.send_heartbeat,
                                  HEARTBEAT_INTERVALL * 1000)

    def stop(self):
        self.proxy = None
        self.queue = Queue()
        self.proc.terminate()

    def send_heartbeat(self):
        if self.proxy:
            self.heartbeat()  # implemented in proxy through __getattr__
            self.set_heartbeat_timer()

    def __getattr__(self, attr):
        '''deletegate all other calls to the xmlrpc client.
        wait if the server process is still runnning, but not responding
        if the server process has died, restart it'''
        def wrapper(*args, **kwargs):
            if not self.proxy:
                self.restart()
                time.sleep(0.2)
            method = getattr(self.proxy, attr)
            result = None
            tries = 0

            # multiple ST3 threads may use the proxy (e.g. linting in parallel
            # to heartbeat etc.) XML-RPC client objects are single-threaded
            # only though, so we introduce a lock here
            with self.rpc_lock:
                while tries < RETRY_CONNECTION_LIMIT:
                    try:
                        result = method(*args, **kwargs)
                        break
                    except Exception:
                        tries += 1
                        if self.proc.poll() is None:
                            # just retry
                            time.sleep(0.2)
                        else:
                            # died, restart and retry
                            self.restart()
                            time.sleep(0.2)
            return result

        return wrapper
    def restart(self):
        ''' (re)starts a Python IDE-server
        this method is complicated by SublimePythonIDE having two different debug modes,
            - one in which the server is started manually by the developer, in which case this
            developer has to set the DEBUG_PORT constant
            - and one case where the server is started automatically but in a verbose mode,
            in which it prints to its stderr, which is copied to ST3's console by an
            AsynchronousFileReader. For this the developer has to set SERVER_DEBUGGING to True
        '''
        try:
            if DEBUG_PORT is not None:
                # debug mode one
                self.port = DEBUG_PORT
                self.proc = DebugProcDummy()
                print("started server on user-defined FIXED port %i with %s" %
                      (self.port, self.python))
            elif SERVER_DEBUGGING:
                # debug mode two
                self.port = self.get_free_port()
                proc_args = [
                    self.python, SERVER_SCRIPT,
                    str(self.port), " --debug"
                ]
                self.proc = subprocess.Popen(proc_args,
                                             cwd=os.path.dirname(self.python),
                                             stderr=subprocess.PIPE,
                                             creationflags=CREATION_FLAGS)
                self.queue = Queue()
                self.stderr_reader = AsynchronousFileReader(
                    "Server on port %i - STDERR" % self.port, self.proc.stderr,
                    self.queue)
                self.stderr_reader.start()
                sublime.set_timeout_async(self.debug_consume, 1000)
                print("started server on port %i with %s IN DEBUG MODE" %
                      (self.port, self.python))
            else:
                # standard run of the server in end-user mode
                self.port = self.get_free_port()
                proc_args = [self.python, SERVER_SCRIPT, str(self.port)]
                self.proc = subprocess.Popen(proc_args,
                                             cwd=os.path.dirname(self.python),
                                             creationflags=CREATION_FLAGS)
                print("started server on port %i with %s" %
                      (self.port, self.python))

            # wait 100 ms to make sure python proc is still running
            for i in range(10):
                time.sleep(0.01)
                if self.proc.poll():
                    if SERVER_DEBUGGING:
                        print(sys.exc_info())
                    raise OSError(
                        None, "Python interpretor crashed (using path %s)" %
                        self.python)

            # in any case, we also need a local client object
            self.proxy = xmlrpc.client.ServerProxy(
                'http://%s:%i' % (self.resolve_localhost(), self.port),
                allow_none=True)
            self.set_heartbeat_timer()
        except OSError as e:
            print("error starting server:", e)
            print(
                "-----------------------------------------------------------------------------------------------"
            )
            print(
                "Try to use an absolute path to your projects python interpreter. On Windows try to use forward"
            )
            print(
                "slashes as in C:/Python27/python.exe or properly escape with double-backslashes"
                "")
            print(
                "-----------------------------------------------------------------------------------------------"
            )
            raise e
예제 #4
0
class Proxy(object):
    '''Abstracts the external Python processes that do the actual
    work. SublimePython just calls local methods on Proxy objects.
    The Proxy objects start external Python processes, send them heartbeat
    messages, communicate with them and restart them if necessary.'''
    def __init__(self, python):
        self.python = python
        self.proc = None
        self.proxy = None
        self.port = None
        self.stderr_reader = None
        self.queue = None
        self.restart()

    def get_free_port(self):
        s = socket.socket()
        s.bind(('', 0))
        port = s.getsockname()[1]
        s.close()
        return port

    def restart(self):
        ''' (re)starts a Python IDE-server
        this method is complicated by SublimePythonIDE having two different debug modes,
            - one in which the server is started manually by the developer, in which case this
            developer has to set the DEBUG_PORT constant
            - and one case where the server is started automatically but in a verbose mode,
            in which it prints to its stderr, which is copied to ST3's console by an
            AsynchronousFileReader. For this the developer has to set SERVER_DEBUGGING to True
        '''
        try:
            if DEBUG_PORT is not None:
                # debug mode one
                self.port = DEBUG_PORT
                self.proc = DebugProcDummy()
                print("started server on user-defined FIXED port %i with %s" % (self.port, self.python))
            elif SERVER_DEBUGGING:
                # debug mode two
                self.port = self.get_free_port()
                proc_args = [self.python, SERVER_SCRIPT, str(self.port), " --debug"]
                self.proc = subprocess.Popen(proc_args, cwd=os.path.dirname(self.python),
                                             stderr=subprocess.PIPE, creationflags=CREATION_FLAGS)
                self.queue = Queue()
                self.stderr_reader = AsynchronousFileReader("Server on port %i - STDERR" % self.port,
                                                            self.proc.stderr, self.queue)
                self.stderr_reader.start()
                sublime.set_timeout_async(self.debug_consume, 1000)
                print("started server on port %i with %s IN DEBUG MODE" % (self.port, self.python))
            else:
                # standard run of the server in end-user mode
                self.port = self.get_free_port()
                proc_args = [self.python, SERVER_SCRIPT, str(self.port)]
                self.proc = subprocess.Popen(proc_args, cwd=os.path.dirname(self.python),
                                             creationflags=CREATION_FLAGS)
                print("started server on port %i with %s" % (self.port, self.python))

            # wait 100 ms to make sure python proc is still running
            for i in range(10):
                time.sleep(0.01)
                if self.proc.poll():
                    if SERVER_DEBUGGING:
                        print(sys.exc_info())
                    raise OSError(None, "Python interpretor crashed (using path %s)" % self.python)

            # in any case, we also need a local client object
            self.proxy = xmlrpc.client.ServerProxy(
                'http://localhost:%i' % self.port, allow_none=True)
            self.set_heartbeat_timer()
        except OSError as e:
            print("error starting server:", e)
            print("-----------------------------------------------------------------------------------------------")
            print("Try to use an absolute path to your projects python interpreter. On Windows try to use forward")
            print("slashes as in C:/Python27/python.exe or properly escape with double-backslashes""")
            print("-----------------------------------------------------------------------------------------------")
            raise e

    def debug_consume(self):
        '''
        If SERVER_DEBUGGING is enabled, is called by ST every 1000ms and prints
        output from server debugging readers.
        '''
        # Check the queues if we received some output (until there is nothing more to get).
        while not self.queue.empty():
            line = self.queue.get()
            print(str(line))
        # Sleep a bit before asking the readers again.
        sublime.set_timeout_async(self.debug_consume, 1000)

    def set_heartbeat_timer(self):
        sublime.set_timeout_async(
            self.send_heartbeat, HEARTBEAT_INTERVALL * 1000)

    def stop(self):
        self.proxy = None
        self.queue = Queue()
        self.proc.terminate()

    def send_heartbeat(self):
        if self.proxy:
            self.proxy.heartbeat()
            self.set_heartbeat_timer()

    def __getattr__(self, attr):
        '''deletegate all other calls to the xmlrpc client.
        wait if the server process is still runnning, but not responding
        if the server process has died, restart it'''
        def wrapper(*args, **kwargs):
            if not self.proxy:
                self.restart()
                time.sleep(0.2)
            method = getattr(self.proxy, attr)
            result = None
            tries = 0
            while tries < RETRY_CONNECTION_LIMIT:
                try:
                    result = method(*args, **kwargs)
                    break
                except Exception:
                    tries += 1
                    if self.proc.poll() is None:
                        # just retry
                        time.sleep(0.2)
                    else:
                        # died, restart and retry
                        self.restart()
                        time.sleep(0.2)
            return result
        return wrapper