def __init__(self,
              kernel=None,
              initfile=None,
              consumer=None,
              kernel_loglevel=logging.NOTSET,
              stdin=PIPE,
              stdout=PIPE,
              stderr=PIPE,
              **kwargs):
     self.id = _thread_counter()
     super().__init__(name='wolfram-kernel-%i' % self.id)
     if kernel is None:
         kernel = self.default_kernel_path()
     if isinstance(kernel, six.string_types):
         if not os.isfile(kernel):
             raise WolframKernelException('Kernel not found at %s.' %
                                          kernel)
         elif not os.access(kernel, os.X_OK):
             raise WolframKernelException('Cannot execute kernel %s.' %
                                          kernel)
         else:
             self.kernel = kernel
     else:
         raise ValueError(
             'Invalid kernel value. Expecting a filepath as a string.')
     if initfile is None:
         self.initfile = os.path_join(os.dirname(__file__), 'initkernel.m')
     else:
         self.initfile = initfile
     if not os.isfile(self.initfile):
         raise FileNotFoundError(
             'Kernel initialization file %s not found.' % self.initfile)
     if logger.isEnabledFor(logging.DEBUG):
         logger.debug('Initializing kernel %s using script: %s' %
                      (self.kernel, self.initfile))
     self.tasks_queue = Queue()
     self.kernel_socket_in = None
     self.kernel_socket_out = None
     self.kernel_proc = None
     self.consumer = consumer
     self.loglevel = kernel_loglevel
     self.kernel_logger = None
     self.evaluation_count = 0
     self._stdin = stdin
     self._stdout = stdout
     self._stderr = stderr
     # some parameters may be passed as kwargs
     self.parameters = {}
     for k, v in kwargs.items():
         try:
             self.set_parameter(k, v)
         # ignore kwargs unknowns key
         except KeyError:
             pass
     # this is a state: this event is set when the kernel will not serve any more evaluation.
     self._state_terminated = False
     # lock controlling concurrent access to the state above.
     self._state_lock = RLock()
     # this is a trigger that will abort most blocking operations.
     self.trigger_termination_requested = Event()
예제 #2
0
    async def start(self):
        """ Start a pool of kernels and wait for at least one of them to 
        be ready for evaluation.

        This method is a coroutine.
        If not all the kernels were able to start, it fails and terminates the pool.
        """
        self.stopped = False
        # keep track of the init tasks. We have to wait before terminating.
        self._pending_init_tasks = {
            (asyncio.ensure_future(self._async_start_kernel(kernel),
                                   loop=self._loop))
            for kernel in self._evaluators
        }
        # uninitialized kernels are removed if they failed to start
        # if they do start the task (the loop) is added to _started_tasks.
        # we need at least one working kernel.
        # we also need to keep track of start kernel tasks in case of early termination.
        while len(self._started_tasks) == 0:
            if len(self._pending_init_tasks) == 0:
                raise WolframKernelException("Failed to start any kernel.")
            _, self._pending_init_tasks = await asyncio.wait(
                self._pending_init_tasks, return_when=asyncio.FIRST_COMPLETED)
        logger.info("Pool initialized with %i running kernels",
                    len(self._started_tasks))
    def _kernel_start(self):
        """Start a new kernel process and open sockets to communicate with it."""
        # Socket to which we push new expressions for evaluation.
        if self.kernel_socket_out is None:
            self.kernel_socket_out = Socket(zmq_type=zmq.PUSH)
        if self.kernel_socket_in is None:
            self.kernel_socket_in = Socket(zmq_type=zmq.PULL)
        # start the evaluation zmq sockets
        self.kernel_socket_out.bind()
        self.kernel_socket_in.bind()
        if logger.isEnabledFor(logging.INFO):
            logger.info('Kernel writes commands to socket: %s',
                        self.kernel_socket_out)
            logger.info(
                'Kernel receives evaluated expressions from socket: %s',
                self.kernel_socket_in)
        # start the kernel process
        cmd = [self.kernel, '-noprompt', "-initfile", self.initfile]
        if self.loglevel != logging.NOTSET:
            self.kernel_logger = KernelLogger(name='wolfram-kernel-logger-%i' %
                                              self.id,
                                              level=self.loglevel)
            self.kernel_logger.start()
            cmd.append('-run')
            cmd.append(
                'ClientLibrary`Private`SlaveKernelPrivateStart["%s", "%s", "%s", %i];'
                % (self.kernel_socket_out.uri, self.kernel_socket_in.uri,
                   self.kernel_logger.socket.uri,
                   FROM_PY_LOG_LEVEL[self.loglevel]))
        else:
            cmd.append('-run')
            cmd.append(
                'ClientLibrary`Private`SlaveKernelPrivateStart["%s", "%s"];' %
                (self.kernel_socket_out.uri, self.kernel_socket_in.uri))

        if logger.isEnabledFor(logging.DEBUG):
            logger.debug('Kernel called using command: %s.' % ' '.join(cmd))
        # hide the WolframKernel window.
        if six.WINDOWS and self.get_parameter('HIDE_SUBPROCESS_WINDOW'):
            startupinfo = STARTUPINFO()
            startupinfo.dwFlags |= STARTF_USESHOWWINDOW
        else:
            startupinfo = None
        try:
            self.kernel_proc = Popen(cmd,
                                     stdin=self._stdin,
                                     stdout=self._stdout,
                                     stderr=self._stderr,
                                     startupinfo=startupinfo)
            if logger.isEnabledFor(logging.INFO):
                logger.info('Kernel process started with PID: %s' %
                            self.kernel_proc.pid)
                t_start = time.perf_counter()
        except Exception as e:
            logger.exception(e)
            raise WolframKernelException('Failed to start kernel process.')
        try:
            # First message must be "OK", acknowledging everything is up and running
            # on the kernel side.
            response = self.kernel_socket_in.recv_abortable(
                timeout=self.get_parameter('STARTUP_TIMEOUT'),
                abort_event=_StartEvent(self.kernel_proc,
                                        self.trigger_termination_requested))
            if response == self._KERNEL_OK:
                if logger.isEnabledFor(logging.INFO):
                    logger.info(
                        'Kernel %s is ready. Startup took %.2f seconds.' %
                        (self.pid, time.perf_counter() - t_start))
            else:
                raise WolframKernelException(
                    'Kernel %s failed to start properly.' % self.kernel)
        except (SocketAborted, SocketOperationTimeout) as se:
            if self.kernel_proc.returncode == self._KERNEL_VERSION_NOT_SUPPORTED:
                raise WolframKernelException(
                    'Wolfram kernel version is not supported. Please consult library prerequisites.'
                )
            logger.warning('Socket exception: %s', se)
            raise WolframKernelException(
                'Failed to communicate with kernel: %s.' % self.kernel)
예제 #4
0
    def __init__(self,
                 kernel=None,
                 initfile=None,
                 consumer=None,
                 kernel_loglevel=logging.NOTSET,
                 stdin=PIPE,
                 stdout=PIPE,
                 stderr=PIPE,
                 **kwargs):
        self.id = _thread_counter()
        super().__init__(name="wolfram-kernel-%i" % self.id)

        self.kernel = kernel or self.default_kernel_path()

        if self.kernel:

            if not os.isfile(self.kernel):
                raise WolframKernelException("Kernel not found at %s." %
                                             self.kernel)

            if not os.access(self.kernel, os.X_OK):
                raise WolframKernelException("Cannot execute kernel %s." %
                                             self.kernel)

        else:
            raise WolframKernelException(
                "Cannot locate a kernel automatically. Please provide an explicit kernel path."
            )

        if initfile is None:
            self.initfile = os.path_join(os.dirname(__file__), "initkernel.m")
        else:
            self.initfile = initfile
        if not os.isfile(self.initfile):
            raise FileNotFoundError(
                "Kernel initialization file %s not found." % self.initfile)
        if logger.isEnabledFor(logging.DEBUG):
            logger.debug("Initializing kernel %s using script: %s" %
                         (self.kernel, self.initfile))

        self.tasks_queue = Queue()
        self.kernel_socket_in = None
        self.kernel_socket_out = None
        self.kernel_proc = None
        self.consumer = consumer
        self.loglevel = kernel_loglevel
        self.kernel_logger = None
        self.evaluation_count = 0
        self._stdin = stdin
        self._stdout = stdout
        self._stderr = stderr
        # some parameters may be passed as kwargs
        self.parameters = {}
        for k, v in kwargs.items():
            try:
                self.set_parameter(k, v)
            # ignore kwargs unknowns key
            except KeyError:
                pass
        # this is a state: this event is set when the kernel will not serve any more evaluation.
        self._state_terminated = False
        # lock controlling concurrent access to the state above.
        self._state_lock = RLock()
        # this is a trigger that will abort most blocking operations.
        self.trigger_termination_requested = Event()