Embedding to C++: hooking up GNU readline

Alex Farber farber at cpan.org
Wed Mar 1 17:52:05 EST 2000


Hi,

Alex Farber wrote:
> I have a C++ application which embeds Python. To use the GNU readline
> library for input I run a separate thread, which calls readline(">>> "),
> then processes the input and passes the received char array to another
> function. And that function is hooked up to PyOS_ReadlineFunctionPointer.

I am attaching my solution for the case that someone else is interested.
I am not sure if there are any stupid bugs or if it will work with future
Python releases. It tries to emulate the Python command line.

The background: I am writing a 3D-viewer in C++ which uses Qt (not thread 
safe) and some FE library (written by my university; not thread safe). 
Since both Qt and the Python (via FE objects and via OpenGL) access Xlib 
(which is not thread safe on many computers), I have to run Qt and Python 
in the same thread.

This means that I can not set PyOS_ReadlineFunctionPointer and run the
PyRun_InteractiveLoop() because then the Qt would freeze. So I have to
run Readline in a different thread and write to a pipe. Qt will select() 
on that pipe (via QSocketNotifier) and run PyRun_SimpleString().

Now the problem is to know whether there is enough data to be run by
Python (consider "for i in [1,2]:\n"). I was hoping to Py_CompileString()
and then, if more data is needed - save the string for later, otherwise - 
run PyEval_EvalCode().

Now the bad thing is, that I can not say if Py_CompileString() failed
because of "not enough data" or because of "syntax error". That's why
I had to write the attached code. I hope that Mr. van Rossum will 
consider adding some easier interface in the later versions of Python - 
because other people embedding Python can get the same difficulties.

Regards
Alex

/* Emulate readline behaviour for the cases where 
   PyRun_InteractiveOne() or PyRun_InteractiveLoop()
   can not be run (like Qt + Python in one thread). */

/* COMPILE: gcc -I/usr/include/python -I/usr/include/readline 
            -lpython1.5 -lreadline -o rdl rdl.c */

#include <stdio.h>

#include <readline.h>

#include <Python.h>
#include <node.h>
#include <errcode.h>
#include <grammar.h>
#include <parsetok.h>
#include <compile.h>

extern grammar _PyParser_Grammar;                          /* from graminit.c */

int main (int argc, char* argv[])
{
  node* n;                                                 /* will these work */
  perrdetail e;                                            /* in future releases? */
  
  int done = 0;
  char *line;                                              /* temporary buffer */
  char *code = NULL;                                       /* source code */
  int i, j;                                                /* lengths of line, code */
  char ps1[] = ">>> ";
  char ps2[] = "... ";
  char *prompt = ps1;
  
  Py_Initialize ();

  while (!done) {
    
    line = readline (prompt);

    if (NULL == line ||                                    /* CTRL-D pressed */
        0 == strcmp (line, "quit") ||
        0 == strcmp (line, "exit")) {

      done = 1;
    }
    else {

      i = strlen (line);

      if (i > 0)
        add_history (line);                                /* save non-empty lines */

      if (NULL == code)                                    /* nothing in code yet */
        j = 0;
      else
        j = strlen (code);

      code = realloc (code, i + j + 2);
      if (NULL == code)                                    /* out of memory */
        exit (1);
     
      if (0 == j)                                          /* code was empty, so */
        code[0] = '\0';                                    /* keep strncat happy */

      strncat (code, line, i);                             /* append line to code */
      code[i + j] = '\n';
      code[i + j + 1] = '\0';                              /* append '\n' to code */

      n = PyParser_ParseString (code, &_PyParser_Grammar, Py_file_input, &e);
    
      if (NULL == n && E_EOF == e.error) {                 /* more input needed */

        prompt = ps2;
      }
      else {                                               /* try to run the code */
        
        PyNode_Free (n);

        if (ps1  == prompt ||                              /* ">>> " or */
            '\n' == code[i + j - 1]) {                     /* "... " and 2 '\n' */
            
          PyRun_SimpleString (code);                       /* returns -1 on failure */
          free (code);
          code = NULL;
          prompt = ps1;
        }
      }
    }

    free (line);
  }
  
  Py_Finalize();
  exit(0);
}



More information about the Python-list mailing list