def _get_doc(self, name): """ Get the documentation of an Octave procedure or object. Parameters ---------- name : str Function name to search for. Returns ------- out : str Documentation string. Raises ------ Oct2PyError If the procedure or object does not exist. """ if name == 'keyboard': return 'Built-in Function: keyboard ()' exist = self.eval('exist {0}'.format(name), log=False, verbose=False) if exist == 0: msg = 'Name: "%s" does not exist on the Octave session path' raise Oct2PyError(msg % name) doc = 'No documentation for %s' % name try: doc, _ = self.eval('help {0}'.format(name), log=False, verbose=False, return_both=True) except Oct2PyError as e: if 'syntax error' in str(e): raise (e) doc, _ = self.eval('type("{0}")'.format(name), log=False, verbose=False, return_both=True) if isinstance(doc, list): doc = doc[0] doc = '\n'.join(doc.splitlines()[:3]) default = self._call.__doc__ doc += '\n' + '\n'.join([line[8:] for line in default.splitlines()]) return doc
def push(self, name, var, verbose=False, timeout=None): """ Put a variable or variables into the Octave session. Parameters ---------- name : str or list Name of the variable(s). var : object or list The value(s) to pass. timeout : float Time to wait for response from Octave (per character). Examples -------- >>> from oct2py import octave >>> y = [1, 2] >>> octave.push('y', y) >>> octave.pull('y') array([[1, 2]]) >>> octave.push(['x', 'y'], ['spam', [1, 2, 3, 4]]) >>> octave.pull(['x', 'y']) # doctest: +SKIP [u'spam', array([[1, 2, 3, 4]])] Notes ----- Integer type arguments will be converted to floating point unless `convert_to_float=False`. """ if isinstance(name, (str, unicode)): vars_ = [var] names = [name] else: vars_ = var names = name for name in names: if name.startswith('_'): raise Oct2PyError('Invalid name {0}'.format(name)) try: tempdir = tempfile.mkdtemp(dir=self.temp_dir) _, load_line = self._writer.create_file(tempdir, vars_, names) self._reader.create_file(tempdir) self.eval(load_line, verbose=verbose, timeout=timeout) finally: shutil.rmtree(tempdir, ignore_errors=True)
def readline(self): t0 = time.time() while 1: try: val = self.read_queue.get_nowait() except queue.Empty: pass else: if val is None: self.close() return else: return val time.sleep(1e-6) if (time.time() - t0) > self.timeout: self.interrupt() raise Oct2PyError('Timed out, interrupting')
def evaluate(self, cmds, logger=None, out_file='', log=True, timeout=None, pre_call='', post_call=''): """Perform the low-level interaction with an Octave Session """ self.logger = logger self.set_timeout(timeout) if not self.proc: raise Oct2PyError('Session Closed, try a restart()') expr = '\n'.join(cmds) if self.first_run: self._handle_first_run() # use ascii code 2 for start of text, 3 for end of text, and # 24 to signal an error output = """ %(pre_call)s clear("ans"); rehash; clear("_"); clear("a__"); disp(char(2)); try disp(char(2)); %(expr)s if exist("ans") == 1 _ = ans; end disp(char(3)) catch disp(lasterr()); disp(char(24)); end if exist("_") == 1 if exist("a__") == 0 save -v6 %(out_file)s _; end end %(post_call)s disp(char(3)) """ % locals() if len(cmds) == 5: main_line = cmds[2].strip() else: main_line = '\n'.join(cmds) if 'keyboard' in expr: self.write('keyboard\n') self.interact() return '' self.write(output + '\n') self.expect(chr(2)) resp = self.expect('%s|error: |parse error:' % chr(2)) if 'parse error:' in resp: resp = [resp[resp.index('parse error:'):]] elif 'error:' in resp: resp = [resp[resp.index('error:'):]] else: resp = [] while 1: line = self.readline() if chr(3) in line: break elif chr(24) in line: msg = ('Oct2Py tried to run:\n"""\n{0}\n"""\n' 'Octave returned:\n{1}'.format(main_line, '\n'.join(resp))) self.expect(chr(3)) raise Oct2PyError(msg) elif '\x1b[C' in line or line.strip() == '>>': line = '' elif line.endswith('> '): self.interact(line) elif line.startswith(' ') and line.strip() == '^': if sys.platform == 'win32': self.close() raise Oct2PyError('Syntax Error:\n%s' % '\n'.join(resp)) elif logger and log: logger.debug(line) if resp or line: resp.append(line) self.expect(chr(3)) return '\n'.join(resp).rstrip()
def start(self, executable): """ Start an Octave session in a subprocess. Parameters ========== executable : str Name or path to Octave process. Returns ======= out : fid File descriptor for the Octave subprocess Raises ====== Oct2PyError If the session is not opened sucessfully. Notes ===== Options sent to Octave: -q is quiet startup, --braindead is Matlab compatibilty mode. """ errmsg = ('\n\n`octave` not found. Please see documentation at:\n' 'http://blink1073.github.io/oct2py/source/installation.html') ON_POSIX = 'posix' in sys.builtin_module_names if pty: master, slave = pty.openpty() self.wfid, self.rfid = master, master rpipe, wpipe = slave, slave else: self.rfid, wpipe = os.pipe() rpipe, self.wfid = os.pipe() kwargs = dict(close_fds=ON_POSIX, bufsize=0, stdin=rpipe, stderr=wpipe, stdout=wpipe) if not executable: executable = 'octave' if os.name == 'nt': CREATE_NO_WINDOW = 0x08000000 # Windows-specific flags = subprocess.CREATE_NEW_PROCESS_GROUP + CREATE_NO_WINDOW kwargs['creationflags'] = flags args = [executable, '-q', '--braindead'] try: info = subprocess.check_output([executable, '--version']) except OSError: # pragma: no cover raise Oct2PyError(errmsg) if 'version 4' in info.decode('utf-8').lower(): args += ['--no-gui'] try: proc = subprocess.Popen(args, **kwargs) except OSError: # pragma: no cover raise Oct2PyError(errmsg) else: self.reader = _Reader(self.rfid, self.read_queue) return proc
def _call(self, func, *inputs, **kwargs): """ Oct2Py Parameters -------------------------- inputs : array_like Variables to pass to the function. verbose : bool, optional Log Octave output at INFO level. If False, log at DEBUG level. nout : int, optional Number of output arguments. This is set automatically based on the number of return values requested. You can override this behavior by passing a different value. timeout : float, optional Time to wait for response from Octave (per character). plot_dir: str, optional If specificed, save the session's plot figures to the plot directory instead of displaying the plot window. plot_name : str, optional Saved plots will start with `plot_name` and end with "_%%.xxx' where %% is the plot number and xxx is the `plot_format`. plot_format: str, optional The format in which to save the plot. plot_width: int, optional The plot with in pixels. plot_height: int, optional The plot height in pixels. kwargs : dictionary, optional Key - value pairs to be passed as prop - value inputs to the function. The values must be strings or numbers. Returns ----------- out : value Value returned by the function. Raises ---------- Oct2PyError If the function call is unsucessful. Notes ----- Integer type arguments will be converted to floating point unless `convert_to_float=False`. """ nout = kwargs.pop('nout', get_nout()) argout_list = ['_'] # these three lines will form the commands sent to Octave # load("-v6", "infile", "invar1", ...) # [a, b, c] = foo(A, B, C) # save("-v6", "out_file", "outvar1", ...) load_line = call_line = save_line = '' prop_vals = [] eval_kwargs = {} for (key, value) in kwargs.items(): if key in ['verbose', 'timeout'] or key.startswith('plot_'): eval_kwargs[key] = value continue if isinstance(value, (str, unicode, int, float)): prop_vals.append('"%s", %s' % (key, repr(value))) else: msg = 'Keyword arguments must be a string or number: ' msg += '%s = %s' % (key, value) raise Oct2PyError(msg) prop_vals = ', '.join(prop_vals) try: temp_dir = tempfile.mkdtemp(dir=self.temp_dir) self._reader.create_file(temp_dir) if nout: # create a dummy list of var names ("a", "b", "c", ...) # use ascii char codes so we can increment argout_list, save_line = self._reader.setup(nout) call_line = '[{0}] = '.format(', '.join(argout_list)) call_line += func + '(' if inputs: argin_list, load_line = self._writer.create_file( temp_dir, inputs) call_line += ', '.join(argin_list) if prop_vals: if inputs: call_line += ', ' call_line += prop_vals call_line += ');' # create the command and execute in octave cmd = [load_line, call_line, save_line] data = self.eval(cmd, temp_dir=temp_dir, **eval_kwargs) finally: try: shutil.rmtree(temp_dir) except OSError: pass if isinstance(data, dict) and not isinstance(data, Struct): data = [data.get(v, None) for v in argout_list] if len(data) == 1 and data.values()[0] is None: data = None return data
def eval(self, cmds, verbose=True, timeout=None, log=True, temp_dir=None, plot_dir=None, plot_name='plot', plot_format='svg', plot_width=None, plot_height=None, return_both=False): """ Evaluate an Octave command or commands. Parameters ---------- cmds : str or list Commands(s) to pass to Octave. verbose : bool, optional Log Octave output at INFO level. If False, log at DEBUG level. log : bool, optional Whether to log at all. timeout : float, optional Time to wait for response from Octave (per character). plot_dir: str, optional If specificed, save the session's plot figures to the plot directory instead of displaying the plot window. plot_name : str, optional Saved plots will start with `plot_name` and end with "_%%.xxx' where %% is the plot number and xxx is the `plot_format`. plot_format: str, optional The format in which to save the plot (PNG by default). plot_width: int, optional The plot with in pixels. plot_height: int, optional The plot height in pixels. return_both: bool, optional If True, return a (text, value) tuple with the response and the return value. Returns ------- out : object Octave "ans" variable, or None. Raises ------ Oct2PyError If the command(s) fail. """ if not self._session: raise Oct2PyError('No Octave Session') if isinstance(cmds, (str, unicode)): cmds = [cmds] if log: [self.logger.debug(line) for line in cmds] if timeout is None: timeout = self.timeout pre_call, post_call = self._get_plot_commands(plot_dir, plot_format, plot_width, plot_height, plot_name) try: if not temp_dir: temp_dir = tempfile.mkdtemp(dir=self.temp_dir) self._reader.create_file(temp_dir) try: resp = self._session.evaluate(cmds, logger=self.logger, log=log, timeout=timeout, out_file=self._reader.out_file, pre_call=pre_call, post_call=post_call) except KeyboardInterrupt: self._session.interrupt() if os.name == 'nt': self.restart() return 'Octave Session Interrupted, Restarting Session' return 'Octave Session Interrupted' out_file = self._reader.out_file data = None if os.path.exists(out_file) and os.stat(out_file).st_size: try: data = self._reader.extract_file() except (TypeError, IOError) as e: self.logger.debug(e) finally: shutil.rmtree(temp_dir) resp = resp.strip() if resp: if verbose: print(resp) self.logger.info(resp) if return_both: return resp, data else: return data
def start(self, executable): """ Start an Octave session in a subprocess. Parameters ========== executable : str Name or path to Octave process. Returns ======= out : fid File descriptor for the Octave subprocess Raises ====== Oct2PyError If the session is not opened sucessfully. Notes ===== Options sent to Octave: -q is quiet startup. """ errmsg = ('\n\n`octave` not found. Please see documentation at:\n' 'http://blink1073.github.io/oct2py/source/installation.html') ON_POSIX = 'posix' in sys.builtin_module_names if pty: master, slave = pty.openpty() self.wfid, self.rfid = master, master rpipe, wpipe = slave, slave else: self.rfid, wpipe = os.pipe() rpipe, self.wfid = os.pipe() kwargs = dict(close_fds=ON_POSIX, bufsize=0, stdin=rpipe, stderr=wpipe, stdout=wpipe) executable = executable or os.environ.get('OCTAVE', None) executable = executable or os.environ.get('OCTAVE_EXECUTABLE', None) msg = ('Octave Executable not found, please add to path or set' '"OCTAVE_EXECUTABLE" environment variable') if not executable or not which(executable.split()[0]): if which('octave-cli'): executable = 'octave-cli' elif which('octave'): executable = 'octave' else: raise OSError(msg) if os.name == 'nt': CREATE_NO_WINDOW = 0x08000000 # Windows-specific flags = subprocess.CREATE_NEW_PROCESS_GROUP + CREATE_NO_WINDOW kwargs['creationflags'] = flags args = executable.split() args.append('-q') if 'octave-cli' not in executable: try: info = subprocess.check_output([executable[0], '--version']) except OSError: # pragma: no cover raise Oct2PyError(errmsg) if 'version 4' in info.decode('utf-8').lower(): args += ['--no-gui'] try: proc = subprocess.Popen(args, **kwargs) except OSError: # pragma: no cover raise Oct2PyError(errmsg) else: if not pty: self.reader = _Reader(self.rfid, self.read_queue) else: self.reader = _PtyReader(self.rfid, self.read_queue) return proc