Question

J'ai un programme qui, au cours de ce terme a parfois besoin d'appeler python pour préformer certaines tâches. J'ai besoin d'une fonction qui appelle python et prises pythons stdout et met dans un certain dossier. Ceci est une déclaration de la fonction

  pythonCallBackFunc(const char* pythonInput)

Mon problème est d'attraper toutes les sorties python pour une commande donnée (pythonInput). Je n'ai aucune expérience avec l'API python et je ne sais pas quelle est la bonne technique pour le faire. La première chose que j'ai essayé est au sdtout de python et redirect stderr en utilisant Py_run_SimpleString Ceci est un exemple de code que je l'ai écrit.

#include "boost\python.hpp"
#include <iostream>

void pythonCallBackFunc(const char* inputStr){   

    PyRun_SimpleString(inputStr); 
}


int main () {
    ...
   //S0me outside functions does this
   Py_Initialize();
   PyRun_SimpleString("import sys");
   PyRun_SimpleString("old_stdout = sys.stdout");
   PyRun_SimpleString("fsock = open('python_out.log','a')");
   PyRun_SimpleString("sys.stdout = fsock");
   ...

   //my func   
   pythonCallBackFunc("print 'HAHAHAHAHA'");
   pythonCallBackFunc("result = 5");
   pythonCallBackFunc("print result");

   pythonCallBackFunc("result = 'Hello '+'World!'");
   pythonCallBackFunc("print result");

   pythonCallBackFunc("'KUKU '+'KAKA'");
   pythonCallBackFunc("5**3");

   pythonCallBackFunc("prinhghult");

   pythonCallBackFunc("execfile('stdout_close.py')");
   ... 

   //Again anothers function code
   PyRun_SimpleString("sys.stdout = old_stdout");
   PyRun_SimpleString("fsock.close()");

   Py_Finalize();
   return 0;
}

Y at-il une meilleure façon de le faire? Par ailleurs, pour une raison quelconque PyRun_SimpleString ne fait rien quand il obtient une expression mathématique, par exemple PyRun_SimpleString ( "5 ** 3") imprime rien (python conlsul imprime le résultat: 125)

peut-être, il est important, je suis en utilisant Visual Studio 2008. Merci, Alex


Les changements que je l'ai fait selon la suggestion de Mark:

  #include <python.h>
  #include <string>

  using namespace std;

  void PythonPrinting(string inputStr){ 
     string stdOutErr =
    "import sys\n\
     class CatchOut:\n\
        def __init__(self):\n\
           self.value = ''\n\
        def write(self, txt):\n\
           self.value += txt\n\
     catchOut = CatchOut()\n\
     sys.stdout = catchOut\n\
     sys.stderr = catchOut\n\
    "; //this is python code to redirect stdouts/stderr

     PyObject *pModule = PyImport_AddModule("__main__"); //create main module
     PyRun_SimpleString(stdOutErr.c_str()); //invoke code to redirect

     PyRun_SimpleString(inputStr.c_str());
     PyObject *catcher = PyObject_GetAttrString(pModule,"catchOut");

     PyObject *output = PyObject_GetAttrString(catcher,"value");
     printf("Here's the output: %s\n", PyString_AsString(output)); 
     }

  int main(int argc, char** argv){
         Py_Initialize();

     PythonPrinting("print 123");
     PythonPrinting("1+5");
     PythonPrinting("result = 2");
         PythonPrinting("print result");

         Py_Finalize();
         return 0;
  }

La sortie je reçois après l'exécution principale:

 Here's the output: 123

 Here's the output:
 Here's the output: 
 Here's the output: 2

Il est bon pour moi, mais un seul problème, il doit être

 Here's the output: 123

 Here's the output: 6

 Here's the output: 
 Here's the output: 2

Je ne sais pas pourquoi, mais après l'exécution de cette commande: PythonPrinting ( « 1 + 5 »), commande retourne PyString_AsString (sortie) une chaîne vide (char *) au lieu de 6 ... :( Y at-il somthing je peux ne pas de perdre cette sortie?

Thaks, Alex

Était-ce utile?

La solution

Si je lis bien votre question, vous voulez stdout / stderr capture dans une variable au sein de votre C ++? Vous pouvez le faire en redirigeant stdout / stderr dans une variable python, puis en interrogeant cette variable dans votre C ++. S'il vous plaît pas que je ne l'ai pas fait le décompte correct ref ci-dessous:

#include <Python.h>
#include <string>

int main(int argc, char** argv)
{
    std::string stdOutErr =
"import sys\n\
class CatchOutErr:\n\
    def __init__(self):\n\
        self.value = ''\n\
    def write(self, txt):\n\
        self.value += txt\n\
catchOutErr = CatchOutErr()\n\
sys.stdout = catchOutErr\n\
sys.stderr = catchOutErr\n\
"; //this is python code to redirect stdouts/stderr

    Py_Initialize();
    PyObject *pModule = PyImport_AddModule("__main__"); //create main module
    PyRun_SimpleString(stdOutErr.c_str()); //invoke code to redirect
    PyRun_SimpleString("print(1+1)"); //this is ok stdout
    PyRun_SimpleString("1+a"); //this creates an error
    PyObject *catcher = PyObject_GetAttrString(pModule,"catchOutErr"); //get our catchOutErr created above
    PyErr_Print(); //make python print any errors

    PyObject *output = PyObject_GetAttrString(catcher,"value"); //get the stdout and stderr from our catchOutErr object

    printf("Here's the output:\n %s", PyString_AsString(output)); //it's not in our C++ portion

    Py_Finalize();


    return 0;

}

Autres conseils

Voici une C ++ solution amicale que j'ai développé ces derniers temps.

J'explique quelques détails de celui-ci sur mon blog: redirection Python sys.stdout en C ++ où je aussi le point de dépôt à ma GitHub où la version la plus récente se trouve. Voici exemple complet basé sur le code en vigueur au moment de l'affichage de cette réponse:

#include <functional>
#include <iostream>
#include <string>
#include <Python.h>

namespace emb
{

typedef std::function<void(std::string)> stdout_write_type;

struct Stdout
{
    PyObject_HEAD
    stdout_write_type write;
};

PyObject* Stdout_write(PyObject* self, PyObject* args)
{
    std::size_t written(0);
    Stdout* selfimpl = reinterpret_cast<Stdout*>(self);
    if (selfimpl->write)
    {
        char* data;
        if (!PyArg_ParseTuple(args, "s", &data))
            return 0;

        std::string str(data);
        selfimpl->write(str);
        written = str.size();
    }
    return PyLong_FromSize_t(written);
}

PyObject* Stdout_flush(PyObject* self, PyObject* args)
{
    // no-op
    return Py_BuildValue("");
}

PyMethodDef Stdout_methods[] =
{
    {"write", Stdout_write, METH_VARARGS, "sys.stdout.write"},
    {"flush", Stdout_flush, METH_VARARGS, "sys.stdout.flush"},
    {0, 0, 0, 0} // sentinel
};

PyTypeObject StdoutType =
{
    PyVarObject_HEAD_INIT(0, 0)
    "emb.StdoutType",     /* tp_name */
    sizeof(Stdout),       /* tp_basicsize */
    0,                    /* tp_itemsize */
    0,                    /* tp_dealloc */
    0,                    /* tp_print */
    0,                    /* tp_getattr */
    0,                    /* tp_setattr */
    0,                    /* tp_reserved */
    0,                    /* tp_repr */
    0,                    /* tp_as_number */
    0,                    /* tp_as_sequence */
    0,                    /* tp_as_mapping */
    0,                    /* tp_hash  */
    0,                    /* tp_call */
    0,                    /* tp_str */
    0,                    /* tp_getattro */
    0,                    /* tp_setattro */
    0,                    /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT,   /* tp_flags */
    "emb.Stdout objects", /* tp_doc */
    0,                    /* tp_traverse */
    0,                    /* tp_clear */
    0,                    /* tp_richcompare */
    0,                    /* tp_weaklistoffset */
    0,                    /* tp_iter */
    0,                    /* tp_iternext */
    Stdout_methods,       /* tp_methods */
    0,                    /* tp_members */
    0,                    /* tp_getset */
    0,                    /* tp_base */
    0,                    /* tp_dict */
    0,                    /* tp_descr_get */
    0,                    /* tp_descr_set */
    0,                    /* tp_dictoffset */
    0,                    /* tp_init */
    0,                    /* tp_alloc */
    0,                    /* tp_new */
};

PyModuleDef embmodule =
{
    PyModuleDef_HEAD_INIT,
    "emb", 0, -1, 0,
};

// Internal state
PyObject* g_stdout;
PyObject* g_stdout_saved;

PyMODINIT_FUNC PyInit_emb(void) 
{
    g_stdout = 0;
    g_stdout_saved = 0;

    StdoutType.tp_new = PyType_GenericNew;
    if (PyType_Ready(&StdoutType) < 0)
        return 0;

    PyObject* m = PyModule_Create(&embmodule);
    if (m)
    {
        Py_INCREF(&StdoutType);
        PyModule_AddObject(m, "Stdout", reinterpret_cast<PyObject*>(&StdoutType));
    }
    return m;
}

void set_stdout(stdout_write_type write)
{
    if (!g_stdout)
    {
        g_stdout_saved = PySys_GetObject("stdout"); // borrowed
        g_stdout = StdoutType.tp_new(&StdoutType, 0, 0);
    }

    Stdout* impl = reinterpret_cast<Stdout*>(g_stdout);
    impl->write = write;
    PySys_SetObject("stdout", g_stdout);    
}

void reset_stdout()
{
    if (g_stdout_saved)
        PySys_SetObject("stdout", g_stdout_saved);

    Py_XDECREF(g_stdout);
    g_stdout = 0;
}

} // namespace emb

int main()
{
    PyImport_AppendInittab("emb", emb::PyInit_emb);
    Py_Initialize();
    PyImport_ImportModule("emb");

    PyRun_SimpleString("print(\'hello to console\')");

    // here comes the ***magic***
    std::string buffer;
    {
        // switch sys.stdout to custom handler
        emb::stdout_write_type write = [&buffer] (std::string s) { buffer += s; };
        emb::set_stdout(write);
        PyRun_SimpleString("print(\'hello to buffer\')");
        PyRun_SimpleString("print(3.14)");
        PyRun_SimpleString("print(\'still talking to buffer\')");
        emb::reset_stdout();
    }

    PyRun_SimpleString("print(\'hello to console again\')");
    Py_Finalize();

    // output what was written to buffer object
    std::clog << buffer << std::endl;
}

Cela permet d'intercepter la sortie sys.stdout.write avec tout type de appelable C ++ entité: Fonction libre, fonction de membre de classe, des objets de fonction nommée ou même des fonctions anonymes comme dans l'exemple ci-dessus où j'utilise C ++ 11 lambda .

Notez que ceci est un exemple minimal de présenter le concept essentiel. Dans le code de prêt à la production, il a certainement besoin de plus d'attention autour de comptage de référence de PyObject, se débarrasser de l'état global, et ainsi de suite.

Je sais que cette question est ancienne, mais une partie de la question n'a pas été encore répondu:

  

« Comment la sortie de prise de commandes ne pas écrire directement à la sortie standard de Python, comme: 1 + 1 »

Voici les étapes (pour Python 3.4):

  1. Rediriger stdout / stderr dans une variable Python en utilisant la solution de Mark: https://stackoverflow.com/a/4307737/ 1046299

  2. Fonction copie PyRun_InteractiveOneObject(FILE *fp, PyObject *filename, PyCompilerFlags *flags) à partir du code source Python. Il est situé dans le fichier pythonrun.c

  3. Modifier le nom de la fonction PyRun_InteractiveOneObject et signature de telle sorte que la nouvelle fonction prend une const char* (votre commande) en tant que premier paramètre au lieu d'un FILE*. Ensuite, vous devrez utiliser PyParser_ASTFromStringObject au lieu de PyParser_ASTFromFileObject dans la mise en œuvre de la fonction. Notez que vous devrez copier la fonction run_mod est de Python, car il est appelé dans la fonction.

  4. Appelez la nouvelle fonction avec votre commande, par exemple 1+1. Stdout doit maintenant recevoir le 2 de sortie.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top