Example #1
0
    def _proxy(self, *args, **kwargs):
        """Callable replacement for the requested functionality.

        Parameters
        ----------
        *args : list
            Positional arguments to be passed to the remote function.
        **kwargs : dict, optional
            Named arguments to be passed to the remote function.

        Returns
        -------
        object
            The 'data' part of the result dict returned by the remote function.
            The result dict has the following structure ::

                {
                    'error'   : ...,  # A traceback of the error raised on the server, if any.
                    'profile' : ...,  # A profile of the code executed on the server, if there was no error.
                    'data'    : ...,  # The result returned by the target function, if there was no error.
                }

        Warnings
        --------
        The `args` and `kwargs` have to be JSON-serializable.
        This means that, currently, only COMPAS data objects (geometry, robots, data structures) and native Python objects are supported.
        The returned results will also always be in the form of COMPAS data objects and built-in Python objects.
        Numpy objects are automatically converted to their built-in Python equivalents.

        """
        idict = {'args': args, 'kwargs': kwargs}
        istring = json.dumps(idict, cls=DataEncoder)
        # it makes sense that there is a broken pipe error
        # because the process is not the one receiving the feedback
        # when there is a print statement on the server side
        # this counts as output
        # it should be sent as part of RPC communication
        try:
            ostring = self._function(istring)
        except Exception:
            # not clear what the point of this is
            # self.stop_server()
            # if this goes wrong, it means a Fault error was generated by the server
            # no need to stop the server for this
            raise

        if not ostring:
            raise RPCServerError("No output was generated.")

        result = json.loads(ostring, cls=DataDecoder)

        if result['error']:
            raise RPCServerError(result['error'])

        self.profile = result['profile']
        return result['data']
Example #2
0
    def proxy(self, *args, **kwargs):
        """Callable replacement for the requested functionality.

        Parameters
        ----------
        args : list
            Positional arguments to be passed to the remote function.
        kwargs : dict
            Named arguments to be passed to the remote function.

        Returns
        -------
        object
            The result returned by the remote function.

        Warning
        -------
        The `args` and `kwargs` have to be JSON-serialisable.
        This means that, currently, only native Python objects are supported.
        The returned results will also always be in the form of built-in Python objects.

        """
        idict = {'args': args, 'kwargs': kwargs}
        istring = json.dumps(idict, cls=DataEncoder)

        # it makes sense that there is a broken pipe error
        # because the process is not the one receiving the feedback
        # when there is a print statement on the server side
        # this counts as output
        # it should be sent as part of RPC communication
        try:
            ostring = self._function(istring)
        except Exception:
            # not clear what the point of this is
            # self.stop_server()
            # if this goes wrong, it means a Fault error was generated by the server
            # no need to stop the server for this
            raise

        if not ostring:
            raise RPCServerError("No output was generated.")

        result = json.loads(ostring)

        if result['error']:
            raise RPCServerError(result['error'])

        self.profile = result['profile']

        return result['data']
Example #3
0
    def start_server(self):
        """Start the remote server.

        Returns
        -------
        ServerProxy
            Instance of the proxy, if the connection was successful.

        Raises
        ------
        RPCServerError
            If the server providing the requested service cannot be reached after
            100 contact attempts (*pings*).

        """
        env = compas._os.prepare_environment()

        try:
            Popen
        except NameError:
            self._process = Process()

            for name in env:
                if self._process.StartInfo.EnvironmentVariables.ContainsKey(name):
                    self._process.StartInfo.EnvironmentVariables[name] = env[name]
                else:
                    self._process.StartInfo.EnvironmentVariables.Add(name, env[name])

            self._process.StartInfo.UseShellExecute = False
            self._process.StartInfo.RedirectStandardOutput = True
            self._process.StartInfo.RedirectStandardError = True
            self._process.StartInfo.FileName = self.python
            self._process.StartInfo.Arguments = '-m {0} {1}'.format(self.service, str(self._port))
            self._process.Start()
        else:
            args = [self.python, '-m', self.service, str(self._port)]
            self._process = Popen(args, stdout=PIPE, stderr=STDOUT, env=env)

        server = ServerProxy(self.address)

        print("Starting a new proxy server...")

        success = False
        count = 100
        while count:
            try:
                server.ping()
            except:
                time.sleep(0.1)
                count -= 1
                print("    {} attempts left.".format(count))
            else:
                success = True
                break
        if not success:
            raise RPCServerError("The server is not available.")
        else:
            print("New proxy server started.")

        return server
Example #4
0
 def __getattr__(self, name):
     if self.package:
         name = "{}.{}".format(self.package, name)
     try:
         self._function = getattr(self._server, name)
     except:
         raise RPCServerError()
     return self.proxy
Example #5
0
    def proxy(self, *args, **kwargs):
        """Callable replacement for the requested functionality.

        Parameters
        ----------
        args : list
            Positional arguments to be passed to the remote function.
        kwargs : dict
            Named arguments to be passed to the remote function.

        Returns
        -------
        object
            The result returned by the remote function.

        Warning
        -------
        The `args` and `kwargs` have to be JSON-serialisable.
        This means that, currently, only native Python objects are supported.
        The returned results will also always be in the form of built-in Python objects.

        """
        idict = {'args': args, 'kwargs': kwargs}
        istring = json.dumps(idict, cls=DataEncoder)

        try:
            ostring = self._function(istring)
        except:
            self.stop_server()
            raise

        if not ostring:
            raise RPCServerError("No output was generated.")

        result = json.loads(ostring)

        if result['error']:
            raise RPCServerError(result['error'])

        self.profile = result['profile']

        return result['data']
Example #6
0
    def __getattr__(self, name):
        """Find server attributes (methods) corresponding to attributes that do not exist on the proxy itself.

        1. Use :attr:`package` as a namespace for the requested attribute to create a fully qualified path on the server.
        2. Try to get the fully qualified attribute from :attr:`_server`.
        3. If successful, store the result in :attr:`_function`.
        3. Return a handle to :meth:`_proxy`, which will delegate calls to :attr:`_function`.

        Returns
        -------
        callable

        """
        if self.package:
            name = "{}.{}".format(self.package, name)
        try:
            self._function = getattr(self._server, name)
        except Exception:
            raise RPCServerError()
        return self._proxy
Example #7
0
    def start_server(self):
        """Start the remote server.

        Returns
        -------
        ServerProxy
            Instance of the proxy, if the connection was successful.

        Raises
        ------
        RPCServerError
            If the server providing the requested service cannot be reached after
            100 contact attempts (*pings*). The number of attempts is set by
            :attr:`Proxy.max_conn_attempts`.

        Examples
        --------
        >>> p = Proxy()  # doctest: +SKIP
        >>> p.stop_server()  # doctest: +SKIP
        >>> p.start_server()  # doctest: +SKIP

        """
        env = compas._os.prepare_environment()
        # this part starts the server side of the RPC setup
        # it basically launches a subprocess
        # to start the default service
        # the default service creates a server
        # and registers a dispatcher for custom functionality
        try:
            Popen
        except NameError:
            self._process = Process()
            for name in env:
                if self._process.StartInfo.EnvironmentVariables.ContainsKey(
                        name):
                    self._process.StartInfo.EnvironmentVariables[name] = env[
                        name]
                else:
                    self._process.StartInfo.EnvironmentVariables.Add(
                        name, env[name])
            self._process.StartInfo.UseShellExecute = False
            self._process.StartInfo.RedirectStandardOutput = self.capture_output
            self._process.StartInfo.RedirectStandardError = self.capture_output
            self._process.StartInfo.FileName = self.python
            self._process.StartInfo.Arguments = '-m {0} --port {1} --{2}autoreload'.format(
                self.service, self._port, '' if self.autoreload else 'no-')
            self._process.Start()
        else:
            args = [
                self.python, '-m', self.service, '--port',
                str(self._port),
                '--{}autoreload'.format('' if self.autoreload else 'no-')
            ]
            kwargs = dict(env=env)
            if self.capture_output:
                kwargs['stdout'] = PIPE
                kwargs['stderr'] = PIPE

            self._process = Popen(args, **kwargs)
        # this starts the client side
        # it creates a proxy for the server
        # and tries to connect the proxy to the actual server
        server = ServerProxy(self.address)
        print("Starting a new proxy server...")
        success = False
        attempt_count = 0
        while attempt_count < self.max_conn_attempts:
            try:
                server.ping()
            except Exception:
                time.sleep(0.1)
                attempt_count += 1
                print("    {} attempts left.".format(self.max_conn_attempts -
                                                     attempt_count))
            else:
                success = True
                break
        if not success:
            raise RPCServerError("The server is not available.")
        else:
            print("New proxy server started.")
        return server
Example #8
0
    def start_server(self):
        """Start the remote server.

        Returns
        -------
        ServerProxy
            Instance of the proxy, if the connection was successful.

        Raises
        ------
        RPCServerError
            If the server providing the requested service cannot be reached after
            100 contact attempts (*pings*).

        Examples
        --------
        >>> p = Proxy()
        >>> p.stop_server()
        >>> p.start_server()

        """
        env = compas._os.prepare_environment()
        # this part starts the server side of the RPC setup
        # it basically launches a subprocess
        # to start the default service
        # the default service creates a server
        # and registers a dispatcher for custom functionality
        try:
            Popen
        except NameError:
            self._process = Process()

            for name in env:
                if self._process.StartInfo.EnvironmentVariables.ContainsKey(
                        name):
                    self._process.StartInfo.EnvironmentVariables[name] = env[
                        name]
                else:
                    self._process.StartInfo.EnvironmentVariables.Add(
                        name, env[name])
            self._process.StartInfo.UseShellExecute = False
            self._process.StartInfo.RedirectStandardOutput = True
            self._process.StartInfo.RedirectStandardError = True
            self._process.StartInfo.FileName = self.python
            self._process.StartInfo.Arguments = '-m {0} {1}'.format(
                self.service, str(self._port))
            self._process.Start()
        else:
            args = [self.python, '-m', self.service, str(self._port)]
            self._process = Popen(args, stdout=PIPE, stderr=PIPE, env=env)
        # this starts the client side
        # it creates a proxy for the server
        # and tries to connect the proxy to the actual server
        server = ServerProxy(self.address)
        print("Starting a new proxy server...")
        success = False
        count = 100
        while count:
            try:
                server.ping()
            except Exception:
                time.sleep(0.1)
                count -= 1
                print("    {} attempts left.".format(count))
            else:
                success = True
                break
        if not success:
            raise RPCServerError("The server is not available.")
        else:
            print("New proxy server started.")
        return server