Ejemplo n.º 1
0
 def restart(self):
     """Restart an Scilab session in a clean state
     """
     if self._session:
         self._session.close()
     self._reader = MatRead(self._temp_dir)
     self._writer = MatWrite(self._temp_dir, self._oned_as,
                             self._convert_to_float)
     self._session = _Session(self._executable, self._reader.out_file,
                              self.logger)
Ejemplo n.º 2
0
 def restart(self):
     """Restart an Scilab session in a clean state
     """
     self._reader = MatRead(self._temp_dir)
     self._writer = MatWrite(self._temp_dir, self._oned_as)
     self._session = _Session(self._executable,
                              self._reader.out_file, self.logger)
Ejemplo n.º 3
0
 def restart(self):
     """Restart an Scilab session in a clean state
     """
     if self._session:
         self._session.close()
     self._reader = MatRead(self._temp_dir)
     self._writer = MatWrite(self._temp_dir, self._oned_as,
                             self._convert_to_float)
     self._session = _Session(self._executable,
                              self._reader.out_file, self.logger)
Ejemplo n.º 4
0
class Scilab2Py(object):

    """Manages a Scliab session.

    Uses MAT files to pass data between Scilab and Numpy.
    The function must either exist as an file in this directory or
    on Scilab's path.

    You may provide a logger object for logging events, or the scilab.get_log()
    default will be used.  Events will be logged as debug unless verbose is set
    when calling a command, then they will be logged as info.

    Parameters
    ----------
    executable : str, optional
        Name of the Scilab executable, can be a system path.
    logger : logging object, optional
        Optional logger to use for Scilab2Py session
    timeout : float, opional
        Timeout in seconds for commands
    oned_as : {'row', 'column'}, optional
        If 'column', write 1-D numpy arrays as column vectors.
        If 'row', write 1-D numpy arrays as row vectors.}
    temp_dir : str, optional
        If specified, the session's MAT files will be created in the
        directory, otherwise a default directory is used.  This can be
        a shared memory (tmpfs) path.
    """

    def __init__(self, executable=None, logger=None, timeout=None,
                 oned_as='row', temp_dir=None):
        """Start Scilab and create our MAT helpers
        """
        self._oned_as = oned_as
        self._temp_dir = temp_dir or gettempdir()
        self._executable = executable
        atexit.register(lambda: _remove_temp_files(self._temp_dir))
        self.timeout = timeout
        if not logger is None:
            self.logger = logger
        else:
            self.logger = get_log()
        import logging
        self.logger.setLevel(logging.ERROR)
        self._session = None
        self.restart()

    def __enter__(self):
        """Return Scilab object, restart session if necessary"""
        if not self._session:
            self.restart()
        return self

    def __exit__(self, type, value, traceback):
        """Close session"""
        self.exit()

    def exit(self):
        """Closes this Scilab session and removes temp files
        """
        if self._session:
            self._session.close()
        self._session = None
        try:
            self._writer.remove_file()
            self._reader.remove_file()
        except Scilab2PyError as e:
            self.logger.debug(e)

    def push(self, name, var, verbose=False, timeout=None):
        """
        Put a variable or variables into the Scilab 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 Scilab (per character).

        Examples
        --------
        >>> from scilab2py import Scilab2Py
        >>> sci = Scilab2Py()
        >>> y = [1, 2]
        >>> sci.push('y', y)
        >>> sci.pull('y')
        array([[ 1.,  2.]])
        >>> sci.push(['x', 'y'], ['spam', [1., 2., 3., 4.]])
        >>> sci.pull(['x', 'y'])  # doctest: +SKIP
        [u'spam', array([[ 1.,  2.,  3.,  4.]])]

        """
        if isinstance(name, (str, unicode)):
            vars_ = [var]
            names = [name]
        else:
            vars_ = var
            names = name

        for name in names:
            if name.startswith('_'):
                raise Scilab2PyError('Invalid name {0}'.format(name))
        _, load_line = self._writer.create_file(vars_, names)
        self.eval(load_line, verbose=verbose, timeout=timeout)

    def pull(self, var, verbose=False, timeout=None):
        """
        Retrieve a value or values from the Scilab session.

        Parameters
        ----------
        var : str or list
            Name of the variable(s) to retrieve.
        timeout : float
            Time to wait for response from Scilab (per character).

        Returns
        -------
        out : object
            Object returned by Scilab.

        Raises:
          Scilab2PyError
            If the variable does not exist in the Scilab session.

        Examples:
          >>> from scilab2py import Scilab2Py
          >>> sci = Scilab2Py()
          >>> y = [1, 2]
          >>> sci.push('y', y)
          >>> sci.pull('y')
          array([[ 1.,  2.]])
          >>> sci.push(['x', 'y'], ['spam', [1, 2, 3, 4]])
          >>> sci.pull(['x', 'y'])  # doctest: +SKIP
          [u'spam', array([[ 1.,  2.,  3.,  4.]])]

        """
        if isinstance(var, (str, unicode)):
            var = [var]
        argout_list, save_line = self._reader.setup(len(var), var)
        data = self.eval(save_line, verbose=verbose, timeout=timeout)
        if isinstance(data, dict) and not isinstance(data, Struct):
            return [data.get(v, None) for v in argout_list]
        else:
            return data

    def eval(self, cmds, verbose=False, timeout=None, log=True,
             plot_dir=None, plot_name='plot', plot_format='png',
             plot_width=620, plot_height=590):
        """
        Perform Scilab command or commands.

        Parameters
        ----------
        cmd : str or list
            Commands(s) to pass directly to Scilab.
        verbose : bool, optional
             Log Scilab output at info level.
        timeout : float
            Time to wait for response from Scilab (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.

        Returns
        -------
        out : str
            Results printed by Scilab.

        Raises
        ------
        Scilab2PyError
            If the command(s) fail.

        """
        if not self._session:
            raise Scilab2PyError('No Scilab Session')

        if isinstance(cmds, (str, unicode)):
            cmds = [cmds]

        if verbose and log:
            [self.logger.info(line) for line in cmds]

        elif log:
            [self.logger.debug(line) for line in cmds]

        if timeout is None:
            timeout = self.timeout

        for cmd in reversed(cmds):

            match = re.match('([a-z][a-zA-Z0-9_]*) *=', cmd)
            if match and not cmd.strip().endswith(';'):
                cmds.append('ans = %s' % match.groups()[0])
                break

            match = re.match('([a-z][a-zA-Z0-9_]*)\Z', cmd.strip())
            if match and not cmd.strip().endswith(';'):
                cmds.append('ans = %s' % match.groups()[0])
                break

        cmds.append('if (exists("ans")) ; _ans = ans; end')

        pre_call = ''
        post_call = ''

        pre_call += '''
        h = gdf();
         h.figure_position = [0, 0];
         h.figure_size = [%(plot_width)s,%(plot_height)s];
         h.axes_size = [%(plot_width)s * 0.98, %(plot_height)s * 0.8];
        ''' % locals()

        if not plot_dir is None:
            plot_dir = plot_dir.replace("\\", "/")

            spec = '%(plot_dir)s/%(plot_name)s*.%(plot_format)s' % locals()
            existing = glob.glob(spec)
            plot_offset = len(existing)

            pre_call += '''
                close all;
                function handle_all_fig()
                   ids_array=winsid();
                   for i=1:length(ids_array)
                      id=ids_array(i);
                      outfile = sprintf('%(plot_dir)s/__ipy_sci_fig_%%03d', i + %(plot_offset)s);
                      if '%(plot_format)s' == 'jpg' then
                        xs2jpg(id, outfile + '.jpg');
                      elseif '%(plot_format)s' == 'jpeg' then
                        xs2jpg(id, outfile + '.jpeg');
                      elseif '%(plot_format)s' == 'png' then
                        xs2png(id, outfile);
                      else
                        xs2svg(id, outfile);
                      end
                      close(get_figure_handle(id));
                   end
                endfunction
                ''' % locals()

            post_call += 'handle_all_fig();'

        try:
            resp = self._session.evaluate(cmds, verbose, log, self.logger,
                                          timeout=timeout, pre_call=pre_call,
                                          post_call=post_call)
        except KeyboardInterrupt:
            if os.name == 'nt':
                self.restart()
                raise Scilab2PyError('Session Interrupted, Restarting')
            return 'Scilab Session Interrupted'

        outfile = self._reader.out_file
        if os.path.exists(outfile) and os.stat(outfile).st_size:
            try:
                data = self._reader.extract_file()
            except (TypeError, IOError) as e:
                self.logger.debug(e)
            else:
                if data is not None:
                    if verbose and log:
                        self.logger.info(data)
                    elif log:
                        self.logger.debug(data)
                    resp = data

        if not resp in ['', []]:
            return resp

    def restart(self):
        """Restart an Scilab session in a clean state
        """
        self._reader = MatRead(self._temp_dir)
        self._writer = MatWrite(self._temp_dir, self._oned_as)
        self._session = _Session(self._executable,
                                 self._reader.out_file, self.logger)

    # --------------------------------------------------------------
    # Private API
    # --------------------------------------------------------------

    def _make_scilab_command(self, name, doc=None):
        """Create a wrapper to an Scilab procedure or object

        Adapted from the mlabwrap project

        """
        def scilab_command(*args, **kwargs):
            """ Scilab command """
            kwargs['nout'] = get_nout()
            if name == 'getd':
                kwargs['nout'] = 0
            kwargs['verbose'] = kwargs.get('verbose', False)
            return self._call(name, *args, **kwargs)

        # convert to ascii for pydoc
        try:
            doc = doc.encode('ascii', 'replace').decode('ascii')
        except UnicodeDecodeError as e:
            self.logger.debug(e)

        scilab_command.__doc__ = "\n" + doc
        scilab_command.__name__ = name

        return scilab_command

    def _call(self, func, *inputs, **kwargs):
        """
        Scilab2Py Parameters
        ----------
        inputs : array_like
            Variables to pass to the function.
        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.
        verbose : bool, optional
             Log Scilab output at info level.
                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).
        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
        ------
        Scilab2PyError
            If the function call is unsucessful.
        """
        nout = kwargs.pop('nout', get_nout())
        argout_list = ['ans']
        if '=' in func:
            nout = 0

        # these three lines will form the commands sent to Scilab
        # load("-v6", "infile", "invar1", ...)
        # [a, b, c] = foo(A, B, C)
        # save("-v6", "outfile", "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 Scilab2PyError(msg)
        prop_vals = ', '.join(prop_vals)

        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(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, **eval_kwargs)

        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[0] is None:
                data = None

        return data

    def _get_doc(self, name):
        """
        Get the documentation of an Scilab procedure or object.

        Parameters
        ----------
        name : str
            Function name to search for.

        Returns
        -------
        out : str
          Documentation string.

        Raises
        ------
        Scilab2PyError
           If the procedure or object does not exist.

        """
        doc = "No documentation available for `%s`" % name

        try:
            typeof = self.eval('typeof(%s)' % name)

        except Scilab2PyError:
            raise Scilab2PyError('No function named `%s`' % name)

        if typeof == 'fptr':
            doc = "`%s` is a built-in Scilab function." % name

        elif typeof == 'function':

            lines = self.eval('fun2string(%s)' % name)

            if lines and '!' in lines:
                lines = lines[lines.index('!'):]
                lines = lines.replace('!', ' ').splitlines()

                docs = [lines[0].replace('ans(', '%s(' % name), ' ']

                in_doc = False
                for line in lines[1:]:
                    line = line.strip()

                    if line.startswith('//'):
                        docs.append(line[2:])
                        in_doc = True

                    elif in_doc and line:
                        break

                doc = '\n'.join(docs)

        default = self._call.__doc__
        doc += '\n' + '\n'.join([line[8:] for line in default.splitlines()])

        return doc

    def __getattr__(self, attr):
        """Automatically creates a wapper to an Scilab function or object.

        Adapted from the mlabwrap project.

        """
        # needed for help(Scilab2Py())
        if attr == '__name__':
            return super(Scilab2Py, self).__getattr__(attr)
        elif attr == '__file__':
            return __file__
        # close_ -> close
        if attr[-1] == "_":
            name = attr[:-1]
        else:
            name = attr
        doc = self._get_doc(name)
        scilab_command = self._make_scilab_command(name, doc)
        #!!! attr, *not* name, because we might have python keyword name!
        setattr(self, attr, scilab_command)
        return scilab_command
Ejemplo n.º 5
0
class Scilab2Py(object):
    """Manages a Scilab session.

    Uses MAT files to pass data between Scilab and Numpy.
    The function must either exist as an file in this directory or
    on Scilab's path.

    You may provide a logger object for logging events, or the scilab.get_log()
    default will be used.  Events will be logged as debug unless verbose is set
    when calling a command, then they will be logged as info.

    Parameters
    ----------
    executable : str, optional
        Name of the Scilab executable, can be a system path.
    logger : logging object, optional
        Optional logger to use for Scilab2Py session
    timeout : float, opional
        Timeout in seconds for commands
    oned_as : {'row', 'column'}, optional
        If 'column', write 1-D numpy arrays as column vectors.
        If 'row', write 1-D numpy arrays as row vectors.}
    temp_dir : str, optional
        If specified, the session's MAT files will be created in the
        directory, otherwise a default directory is used.  This can be
        a shared memory (tmpfs) path.
    """
    def __init__(self,
                 executable=None,
                 logger=None,
                 timeout=None,
                 oned_as='row',
                 temp_dir=None,
                 convert_to_float=True):
        """Start Scilab and create our MAT helpers
        """
        self._oned_as = oned_as
        self._temp_dir = temp_dir or gettempdir()
        self._executable = executable
        atexit.register(lambda: _remove_temp_files(self._temp_dir))
        self.timeout = timeout
        if not logger is None:
            self.logger = logger
        else:
            self.logger = get_log()
        #self.logger.setLevel(logging.DEBUG)
        self._session = None
        self._convert_to_float = convert_to_float
        self.restart()

    @property
    def convert_to_float(self):
        return self._convert_to_float

    @convert_to_float.setter
    def convert_to_float(self, value):
        self._writer.convert_to_float = value
        self._convert_to_float = value

    def __enter__(self):
        """Return Scilab object, restart session if necessary"""
        if not self._session:
            self.restart()
        return self

    def __exit__(self, type, value, traceback):
        """Close session"""
        self.exit()

    def exit(self):
        """Closes this Scilab session and removes temp files
        """
        if self._session:
            self._session.close()
        self._session = None
        try:
            self._writer.remove_file()
            self._reader.remove_file()
        except Scilab2PyError as e:
            self.logger.debug(e)

    def push(self, name, var, verbose=False, timeout=None):
        """
        Put a variable or variables into the Scilab 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 Scilab (per character).

        Examples
        --------
        >>> from scilab2py import Scilab2Py
        >>> sci = Scilab2Py()
        >>> y = [1, 2]
        >>> sci.push('y', y)
        >>> sci.pull('y')
        array([[ 1.,  2.]])
        >>> sci.push(['x', 'y'], ['spam', [1., 2., 3., 4.]])
        >>> sci.pull(['x', 'y'])  # doctest: +SKIP
        [u'spam', array([[ 1.,  2.,  3.,  4.]])]

        Note
        ----
        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 Scilab2PyError('Invalid name {0}'.format(name))
        _, load_line = self._writer.create_file(vars_, names)
        self.eval(load_line, verbose=verbose, timeout=timeout)

    def pull(self, var, verbose=False, timeout=None):
        """
        Retrieve a value or values from the Scilab session.

        Parameters
        ----------
        var : str or list
            Name of the variable(s) to retrieve.
        timeout : float
            Time to wait for response from Scilab (per character).

        Returns
        -------
        out : object
            Object returned by Scilab.

        Raises:
          Scilab2PyError
            If the variable does not exist in the Scilab session.

        Examples:
          >>> from scilab2py import Scilab2Py
          >>> sci = Scilab2Py()
          >>> y = [1, 2]
          >>> sci.push('y', y)
          >>> sci.pull('y')
          array([[ 1.,  2.]])
          >>> sci.push(['x', 'y'], ['spam', [1, 2, 3, 4]])
          >>> sci.pull(['x', 'y'])  # doctest: +SKIP
          [u'spam', array([[ 1.,  2.,  3.,  4.]])]

        """
        if isinstance(var, (str, unicode)):
            var = [var]
        argout_list, save_line = self._reader.setup(len(var), var)
        data = self.eval(save_line, verbose=verbose, timeout=timeout)
        if isinstance(data, dict) and not isinstance(data, Struct):
            return [data.get(v, None) for v in argout_list]
        else:
            return data

    def eval(self,
             cmds,
             verbose=True,
             timeout=None,
             log=True,
             plot_dir=None,
             plot_name='plot',
             plot_format='png',
             plot_width=620,
             plot_height=590,
             return_both=False):
        """
        Perform Scilab command or commands.

        Parameters
        ----------
        cmd : str or list
            Commands(s) to pass directly to Scilab.
        verbose : bool, optional
             Log Scilab output at info level.
        timeout : float
            Time to wait for response from Scilab (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 an (printed output, value) tuple. If "ans =" is in the printed output,
            the printed output will have that portion removed.

        Returns
        -------
        out : str
            Results printed by Scilab.

        Raises
        ------
        Scilab2PyError
            If the command(s) fail.

        """
        if not self._session:
            raise Scilab2PyError('No Scilab Session')

        if isinstance(cmds, (str, unicode)):
            cmds = [cmds]

        if verbose and log:
            [self.logger.info(line) for line in cmds]

        elif log:
            [self.logger.debug(line) for line in cmds]

        if timeout is None:
            timeout = self.timeout

        pre_call = ''
        post_call = ''

        pre_call += '''
        h = gdf();
         h.figure_position = [0, 0];
         h.figure_size = [%(plot_width)s,%(plot_height)s];
         h.axes_size = [%(plot_width)s * 0.98, %(plot_height)s * 0.8];
        ''' % locals()

        if not plot_dir is None:
            plot_dir = plot_dir.replace("\\", "/")

            spec = '%(plot_dir)s/%(plot_name)s*.%(plot_format)s' % locals()
            existing = glob.glob(spec)
            plot_offset = len(existing)

            pre_call += '''
                close all;
                function handle_all_fig()
                   ids_array=winsid();
                   for i=1:length(ids_array)
                      id=ids_array(i);
                      outfile = sprintf('%(plot_dir)s/__ipy_sci_fig_%%03d', i + %(plot_offset)s);
                      if '%(plot_format)s' == 'jpg' then
                        xs2jpg(id, outfile + '.jpg');
                      elseif '%(plot_format)s' == 'jpeg' then
                        xs2jpg(id, outfile + '.jpeg');
                      elseif '%(plot_format)s' == 'png' then
                        xs2png(id, outfile);
                      else
                        xs2svg(id, outfile);
                      end
                      close(get_figure_handle(id));
                   end
                endfunction
                ''' % locals()

            post_call += 'handle_all_fig();'

        try:
            resp = self._session.evaluate(cmds,
                                          verbose,
                                          log,
                                          self.logger,
                                          timeout=timeout,
                                          pre_call=pre_call,
                                          post_call=post_call)
        except KeyboardInterrupt:
            self.restart()
            raise Scilab2PyError('Session Interrupted, Restarting')

        outfile = self._reader.out_file

        data = None
        if os.path.exists(outfile) and os.stat(outfile).st_size:
            try:
                data = self._reader.extract_file()
            except (TypeError, IOError) as e:
                self.logger.debug(e)

        resp = resp.strip()

        if resp:
            if verbose:
                print(resp)
            self.logger.info(resp)

        if return_both:
            return resp, data
        else:
            return data

    def restart(self):
        """Restart an Scilab session in a clean state
        """
        if self._session:
            self._session.close()
        self._reader = MatRead(self._temp_dir)
        self._writer = MatWrite(self._temp_dir, self._oned_as,
                                self._convert_to_float)
        self._session = _Session(self._executable, self._reader.out_file,
                                 self.logger)

    # --------------------------------------------------------------
    # Private API
    # --------------------------------------------------------------

    def _make_scilab_command(self, name, doc=None):
        """Create a wrapper to an Scilab procedure or object

        Adapted from the mlabwrap project

        """
        def scilab_command(*args, **kwargs):
            """ Scilab command """
            kwargs['nout'] = get_nout()
            if name == 'getd':
                kwargs['nout'] = 0
            kwargs['verbose'] = kwargs.get('verbose', False)
            return self._call(name, *args, **kwargs)

        # convert to ascii for pydoc
        try:
            doc = doc.encode('ascii', 'replace').decode('ascii')
        except UnicodeDecodeError as e:
            self.logger.debug(e)

        scilab_command.__doc__ = "\n" + doc
        scilab_command.__name__ = name

        return scilab_command

    def _call(self, func, *inputs, **kwargs):
        """
        Scilab2Py Parameters
        ----------
        inputs : array_like
            Variables to pass to the function.
        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.
        verbose : bool, optional
             Log Scilab output at info level.
                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).
        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
        ------
        Scilab2PyError
            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 = ['ans']
        if '=' in func:
            nout = 0

        # these three lines will form the commands sent to Scilab
        # load("-v6", "infile", "invar1", ...)
        # [a, b, c] = foo(A, B, C)
        # save("-v6", "outfile", "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 Scilab2PyError(msg)
        prop_vals = ', '.join(prop_vals)

        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(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, **eval_kwargs)

        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[0] is None:
                data = None

        return data

    def _get_doc(self, name):
        """
        Get the documentation of an Scilab procedure or object.

        Parameters
        ----------
        name : str
            Function name to search for.

        Returns
        -------
        out : str
          Documentation string.

        Raises
        ------
        Scilab2PyError
           If the procedure or object does not exist.

        """
        doc = "No documentation available for `%s`" % name

        try:
            typeof = self.eval('typeof(%s)' % name, verbose=False)

        except Scilab2PyError as e:
            raise Scilab2PyError('Could not find function named `%s`' % name)

        if typeof == 'fptr':
            doc = "`%s` is a built-in Scilab function." % name

        elif typeof == 'function':

            lines = self.eval('fun2string(%s)' % name, verbose=False)

            if lines and '!' in lines:
                lines = lines[lines.index('!'):]
                lines = lines.replace('!', ' ').splitlines()

                docs = [lines[0].replace('ans(', '%s(' % name), ' ']

                in_doc = False
                for line in lines[1:]:
                    line = line.strip()

                    if line.startswith('//'):
                        docs.append(line[2:])
                        in_doc = True

                    elif in_doc and line:
                        break

                doc = '\n'.join(docs)

        default = self._call.__doc__
        doc += '\n' + '\n'.join([line[8:] for line in default.splitlines()])

        return doc

    def __getattr__(self, attr):
        """Automatically creates a wapper to an Scilab function or object.

        Adapted from the mlabwrap project.

        """
        # needed for help(Scilab2Py())
        if attr == '__file__':
            return __file__
        if attr.startswith('__'):
            return super(Scilab2Py, self).__getattr__(attr)
        # close_ -> close
        if attr[-1] == "_":
            name = attr[:-1]
        else:
            name = attr
        doc = self._get_doc(name)
        scilab_command = self._make_scilab_command(name, doc)
        #!!! attr, *not* name, because we might have python keyword name!
        setattr(self, attr, scilab_command)
        return scilab_command