web-dev-qa-db-fra.com

Comment appelez-vous le code Python à partir du code C?

Je souhaite étendre un nouveau projet C avec de nouvelles fonctionnalités, mais je souhaite vraiment l'écrire en Python. Fondamentalement, je veux appeler le code Python à partir du code C. Cependant, les wrappers Python-> C tels que SWIG autorisent l'OPPOSITE, c'est-à-dire l'écriture de modules C et l'appel de C à partir de Python.

J'envisage une approche impliquant IPC ou RPC (les processus multiples ne me dérangent pas); c'est-à-dire que mon composant pur-Python s'exécute dans un processus séparé (sur la même machine) et que mon projet C communique avec lui en écrivant/lisant à partir d'un socket (ou d'un tube Unix). mon composant python peut lire/écrire dans un socket pour communiquer. Est-ce une approche raisonnable? Y a-t-il quelque chose de mieux? Vous aimez un mécanisme spécial RPC?

Merci pour la réponse jusqu’à présent - Cependant, je voudrais me concentrer sur les approches basées sur la CIB car je souhaite intégrer mon programme Python à un processus distinct, en tant que programme C. Je ne veux pas intégrer un interprète Python. Merci!

34
pgb

Je recommande les approches détaillées ici . Il commence par expliquer comment exécuter des chaînes de code Python, puis explique comment configurer un environnement Python pour qu'il interagisse avec votre programme C, appelle des fonctions Python à partir de votre code C, manipule des objets Python à partir de votre code C, etc.

MODIFIER: Si vous voulez vraiment suivre la voie de l'IPC, alors vous voudrez utiliser le module struct ou, mieux encore, protlib . La plupart des communications entre un processus Python et C tournent autour du passage de structures, soit sur un socket , soit à travers la mémoire partagée .

Je recommande de créer une structure Command avec des champs et des codes pour représenter les commandes et leurs arguments. Je ne peux pas donner beaucoup plus de conseils spécifiques sans en savoir plus sur ce que vous voulez accomplir, mais en général, je recommande la bibliothèque protlib , car c'est ce que j'utilise pour communiquer entre les programmes C et Python auteur de protlib).

11
Eli Courtwright

Voir le chapitre correspondant dans le manuel: http://docs.python.org/extending/

Pour l’essentiel, vous devez intégrer l’interprète python dans votre programme.

4
janneb

Avez-vous envisagé de placer votre application Python dans un script Shell et de l'invoquer avec dans votre application C?

Ce n’est pas la solution la plus élégante, mais c’est très simple.

4
hhafez

Je n'ai pas utilisé d'approche IPC pour la communication Python <-> C, mais cela devrait fonctionner assez bien. J'aurais le programme C faire un exec standard fork et utiliser redirigé stdin et stdout dans le processus enfant pour la communication. Une bonne communication textuelle facilitera le développement et le test du programme Python.

1
D.Shawley

Si j’avais décidé d’utiliser IPC, je ferais probablement des folies avec XML-RPC - multiplate-forme, vous permettant de placer facilement le projet de serveur Python sur un autre nœud ultérieurement si vous le souhaitez, possède de nombreuses excellentes implémentations voir ici pour beaucoup, y compris ceux en C et Python, et ici pour le simple serveur XML-RPC qui fait partie de la bibliothèque standard Python - pas aussi évolutif que d’autres approches, mais probablement très bien et pratique pour votre cas d'utilisation).

Ce n'est peut-être pas une approche parfaite IPC - dans tous les cas (ni même une solution RPC parfaite, à tout prix!), Mais la commodité, la flexibilité, la robustesse et le large éventail de mises en œuvre dépassent les défauts mineurs , À mon avis.

1
Alex Martelli

J'ai utilisé l'approche "standard" de Intégration de Python dans une autre application . Mais c'est compliqué/fastidieux. Chaque nouvelle fonction en Python est pénible à implémenter.

J'ai vu un exemple de Calling PyPy de C . Il utilise CFFI pour simplifier l'interface, mais nécessite PyPy, pas Python. Lisez et comprenez d'abord cet exemple, du moins à un niveau élevé.

J'ai modifié l'exemple C/PyPy pour qu'il fonctionne avec Python. Voici comment appeler Python depuis C en utilisant CFFI. 

Mon exemple est plus compliqué car j'ai implémenté trois fonctions en Python au lieu d'une. Je voulais aborder d'autres aspects de la transmission des données. 

La partie compliquée est maintenant isolée pour passer l'adresse de api à Python. Cela ne doit être mis en œuvre qu'une seule fois. Après cela, il est facile d’ajouter de nouvelles fonctions en Python.

interface.h

// These are the three functions that I implemented in Python.
// Any additional function would be added here.
struct API {
    double (*add_numbers)(double x, double y);
    char* (*dump_buffer)(char *buffer, int buffer_size);
    int (*release_object)(char *obj);
};

test_cffi.c

//
// Calling Python from C.
// Based on Calling PyPy from C:
// http://doc.pypy.org/en/latest/embedding.html#more-complete-example
//

#include <stdio.h>
#include <assert.h>

#include "Python.h"

#include "interface.h"

struct API api;   /* global var */

int main(int argc, char *argv[])
{
    int rc;

    // Start Python interpreter and initialize "api" in interface.py using 
    // old style "Embedding Python in Another Application":
    // https://docs.python.org/2/extending/embedding.html#embedding-python-in-another-application
    PyObject *pName, *pModule, *py_results;
    PyObject *fill_api;
#define PYVERIFY(exp) if ((exp) == 0) { fprintf(stderr, "%s[%d]: ", __FILE__, __LINE__); PyErr_Print(); exit(1); }

    Py_SetProgramName(argv[0]);  /* optional but recommended */
    Py_Initialize();
    PyRun_SimpleString(
            "import sys;"
            "sys.path.insert(0, '.')" );

    PYVERIFY( pName = PyString_FromString("interface") )
    PYVERIFY( pModule = PyImport_Import(pName) )
    Py_DECREF(pName);
    PYVERIFY( fill_api = PyObject_GetAttrString(pModule, "fill_api") )

    // "k" = [unsigned long],
    // see https://docs.python.org/2/c-api/arg.html#c.Py_BuildValue
    PYVERIFY( py_results = PyObject_CallFunction(fill_api, "k", &api) )
    assert(py_results == Py_None);

    // Call Python function from C using cffi.
    printf("sum: %f\n", api.add_numbers(12.3, 45.6));

    // More complex example.
    char buffer[20];
    char * result = api.dump_buffer(buffer, sizeof buffer);
    assert(result != 0);
    printf("buffer: %s\n", result);

    // Let Python perform garbage collection on result now.
    rc = api.release_object(result);
    assert(rc == 0);

    // Close Python interpreter.
    Py_Finalize();

    return 0;
}

interface.py

import cffi
import sys
import traceback

ffi = cffi.FFI()
ffi.cdef(file('interface.h').read())

# Hold references to objects to prevent garbage collection.
noGCDict = {}

# Add two numbers.
# This function was copied from the PyPy example.
@ffi.callback("double (double, double)")
def add_numbers(x, y):
    return x + y

# Convert input buffer to repr(buffer).
@ffi.callback("char *(char*, int)")
def dump_buffer(buffer, buffer_len):
    try:
        # First attempt to access data in buffer.
        # Using the ffi/lib objects:
        # http://cffi.readthedocs.org/en/latest/using.html#using-the-ffi-lib-objects
        # One char at time, Looks inefficient.
        #data = ''.join([buffer[i] for i in xrange(buffer_len)])

        # Second attempt.
        # FFI Interface:
        # http://cffi.readthedocs.org/en/latest/using.html#ffi-interface
        # Works but doc says "str() gives inconsistent results".
        #data = str( ffi.buffer(buffer, buffer_len) )

        # Convert C buffer to Python str.
        # Doc says [:] is recommended instead of str().
        data = ffi.buffer(buffer, buffer_len)[:]

        # The goal is to return repr(data)
        # but it has to be converted to a C buffer.
        result = ffi.new('char []', repr(data))

        # Save reference to data so it's not freed until released by C program.
        noGCDict[ffi.addressof(result)] = result

        return result
    except:
        print >>sys.stderr, traceback.format_exc()
        return ffi.NULL

# Release object so that Python can reclaim the memory.
@ffi.callback("int (char*)")
def release_object(ptr):
    try:
        del noGCDict[ptr]
        return 0
    except:
        print >>sys.stderr, traceback.format_exc()
        return 1

def fill_api(ptr):
    global api
    api = ffi.cast("struct API*", ptr)

    api.add_numbers = add_numbers
    api.dump_buffer = dump_buffer
    api.release_object = release_object

Compiler:

gcc -o test_cffi test_cffi.c -I/home/jmudd/pgsql-native/Python-2.7.10.install/include/python2.7 -L/home/jmudd/pgsql-native/Python-2.7.10.install/lib -lpython2.7

Exécuter:

$ test_cffi
sum: 57.900000
buffer: 'T\x9e\x04\x08\xa8\x93\xff\xbf]\x86\x04\x08\x00\x00\x00\x00\x00\x00\x00\x00'
$ 
0
JohnMudd

apparemment, Python a besoin de pouvoir compiler pour dll win32, cela résoudra le problème

De telle manière que la conversion du code c # en dll win32 le rende utilisable par tout outil de développement

0
Mandrake

Cela semble assez sympa http://thrift.Apache.org/ , il existe même un livre à ce sujet.

Détails:

Le cadre logiciel Apache Thrift, pour un langage multilingue évolutif développement des services, combine une pile de logiciels avec une génération de code moteur pour construire des services qui fonctionnent efficacement et de manière transparente entre C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C #, Cocoa, JavaScript, Node.js, Smalltalk, OCaml et Delphi et d’autres langages.

0
jhegedus