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()
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)
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()