2017-12-01 6 views
5

Ich versuche, einen bestehenden Python-Code und einen neuen C++ 11-Code mit CMake und pybind 11 zusammen zu packen. Ich denke, ich vermisse etwas Einfaches in CMake-Skripten hinzuzufügen, kann es aber nirgends finden: pybind11-Beispiele haben nur C++ Code und nichts von Python, andere Online-Ressourcen sind eher verworren und nicht auf dem neuesten Stand - so kann ich einfach nicht herausfinden, wie man Funktionen in beiden Sprachen zusammenpackt und sie über Pythons import my_package auf der ganzen Linie verfügbar macht ... als Beispiel habe ich die cmake_example von pybind11 und added a mult function in cmake_example/mult.pypybind11: Wie packt man C++ - und Python-Code in ein einziges Paket?

def mult(a, b): 
    return a * b 

geklont, wie würde ich es zusammen mit add und subtract passieren die t sichtbar machen est unten?

import cmake_example as m 

assert m.__version__ == '0.0.1' 
assert m.add(1, 2) == 3 
assert m.subtract(1, 2) == -1 
assert m.mult(2, 2) == 4 

derzeit, dieser Test fails..

Dank!

Antwort

3

Die einfachste Lösung hat nichts zu tun mit pybind11 als solche. Was Autoren normalerweise tun, wenn sie reine Python und C/Cython/andere native Erweiterungen im selben Paket kombinieren wollen, ist folgendes.

Sie erstellen zwei Module.

  1. mymodule ist eine öffentliche Schnittstelle, eine reine Python-Modul
  2. _mymodule eine private Implementierung ist ein erfüllter Modul

Dann in mymodule Sie notwendige Symbole aus _mymoudle (und Rückgriff auf reine Python-Version importieren Falls benötigt).

Hier Beispiel aus yarl Paket:

  1. quoting.py

    try: 
        from ._quoting import _quote, _unquote 
        quote = _quote 
        unquote = _unquote 
    except ImportError: # pragma: no cover 
        quote = _py_quote 
        unquote = _py_unquote 
    
  2. _quoting.pyx

aktualisieren

Hier folgt das Skript. Aus Gründen der Reproduzierbarkeit tue ich es gegen Original cmake_example.

git clone --recursive https://github.com/pybind/cmake_example.git 
# at the time of writing https://github.com/pybind/cmake_example/commit/8818f493 
cd cmake_example 

Jetzt reine Python-Module erstellen (innen cmake_example/cmake_example).

cmake_example/__init__.py

"""Root module of your package""" 

cmake_example/math.py

def mul(a, b): 
    """Pure Python-only function""" 
    return a * b 


def add(a, b): 
    """Fallback function"""  
    return a + b  

try: 
    from ._math import add 
except ImportError: 
    pass 

Lassen Sie uns jetzt vorhandene Dateien modifizieren cmake_example Modul in cmake_example._math einzuschalten.

src/main.cpp (subtract der Kürze entfernt)

#include <pybind11/pybind11.h> 

int add(int i, int j) { 
    return i + j; 
} 

namespace py = pybind11; 

PYBIND11_MODULE(_math, m) { 
    m.doc() = R"pbdoc(
     Pybind11 example plugin 
     ----------------------- 

     .. currentmodule:: _math 

     .. autosummary:: 
      :toctree: _generate 

      add 
    )pbdoc"; 

    m.def("add", &add, R"pbdoc(
     Add two numbers 

     Some other explanation about the add function. 
    )pbdoc"); 

#ifdef VERSION_INFO 
    m.attr("__version__") = VERSION_INFO; 
#else 
    m.attr("__version__") = "dev"; 
#endif 
} 

CMakeLists.txt

cmake_minimum_required(VERSION 2.8.12) 
project(cmake_example) 

add_subdirectory(pybind11) 
pybind11_add_module(_math src/main.cpp) 

setup.py

# the above stays intact 

from subprocess import CalledProcessError 

kwargs = dict(
    name='cmake_example', 
    version='0.0.1', 
    author='Dean Moldovan', 
    author_email='[email protected]', 
    description='A test project using pybind11 and CMake', 
    long_description='', 
    ext_modules=[CMakeExtension('cmake_example._math')], 
    cmdclass=dict(build_ext=CMakeBuild), 
    zip_safe=False, 
    packages=['cmake_example'] 
) 

# likely there are more exceptions, take a look at yarl example 
try: 
    setup(**kwargs)   
except CalledProcessError: 
    print('Failed to build extension!') 
    del kwargs['ext_modules'] 
    setup(**kwargs) 

Jetzt können wir es bauen.

python setup.py bdist_wheel 

In meinem Fall erzeugt es dist/cmake_example-0.0.1-cp27-cp27mu-linux_x86_64.whl (wenn C++ Kompilation es ist cmake_example-0.0.1-py2-none-any.whl ausfällt). Hier ist, was es Inhalt (unzip -l ...):

Archive: cmake_example-0.0.1-cp27-cp27mu-linux_x86_64.whl 
    Length  Date Time Name 
--------- ---------- ----- ---- 
     0 2017-12-05 21:42 cmake_example/__init__.py 
    81088 2017-12-05 21:43 cmake_example/_math.so 
     223 2017-12-05 21:46 cmake_example/math.py 
     10 2017-12-05 21:48 cmake_example-0.0.1.dist-info/DESCRIPTION.rst 
     343 2017-12-05 21:48 cmake_example-0.0.1.dist-info/metadata.json 
     14 2017-12-05 21:48 cmake_example-0.0.1.dist-info/top_level.txt 
     105 2017-12-05 21:48 cmake_example-0.0.1.dist-info/WHEEL 
     226 2017-12-05 21:48 cmake_example-0.0.1.dist-info/METADATA 
     766 2017-12-05 21:48 cmake_example-0.0.1.dist-info/RECORD 
---------      ------- 
    82775      9 files 
+0

danke, das ist ein sehr nützlicher Vorschlag. ich bin in python und seinem packaging toolkit nicht kompetent genug, die vorgeschlagene lösung alleine zu implementieren, selbst für dieses einfache beispiel, das ich auf den github gesetzt habe.Könntest du mir helfen, indem du es formulierst, um das "Mult" in C++ und ein Fallback in Python zu haben, wie du es vorgeschlagen hast? also kann ich es als Ausgangspunkt für den Aufbau und die Verteilung meines eigenen Pakets verwenden? Danke! – seninp

+0

@seninp Werfen Sie einen Blick darauf. – saaj

+0

Ich mag die Funktionsweise von C++ - und Python-Quellen und die Möglichkeit, diese unabhängig voneinander zu testen und zu verwenden. Außerdem erstellt und testet es nahtlos auf Travis und Codecov kann die Coverage-Metriken abrufen. Die Antwort annehmen - Danke nochmal! – seninp

2

Sobald Sie den Repo-cd in Top-Level-Verzeichnis `cmake_example‘

ändern ./src/main.cpp umfassen eine "mult" -Funktion geklont:

#include <pybind11/pybind11.h> 

int add(int i, int j) { 
    return i + j; 
} 

int mult(int i, int j) { 
    return i * j; 
} 

namespace py = pybind11; 

PYBIND11_MODULE(cmake_example, m) { 
    m.doc() = R"pbdoc(
     Pybind11 example plugin 
     ----------------------- 

     .. currentmodule:: cmake_example 

     .. autosummary:: 
      :toctree: _generate 

      add 
      subtract 
      mult 

    )pbdoc"; 

    m.def("add", &add, R"pbdoc(
     Add two numbers 

     Some other explanation about the add function. 
    )pbdoc"); 

    m.def("mult", &mult, R"pbdoc(
     Multiply two numbers 

     Some other explanation about the mult function. 
    )pbdoc"); 

(den Rest die Datei das gleiche ist)

Jetzt machen:

$ cmake -H. -Bbuild 
$ cmake --build build -- -j3 

das Modul für den Import wird cre im Verzeichnis ./build gespeichert. Gehe dazu, dann sollte dein Beispiel in einer Python-Shell funktionieren.

für den Namespace Import, könnten Sie etwas mit pkgutil tun:

die Verzeichnisstruktur erstellen:

./my_mod 
    __init__.py 
    cmake_example.***.so 

und andere parallele Struktur

./extensions 
    /my_mod 
     __init__.py 
     cmake_example_py.py 

und in ./my_mod/__init__.py

import pkgutil 
__path__ = pkgutil.extend_path(__path__, __name__) 

from .cmake_example import add, subtract 
from .cmake_example_py import mult 

in ./extensions/my_mod/__init__.py

from cmake_example_py import mult 

dann beide ./my_mod und ./extensions/my_mod zu Ihrem $ PYTHONPATH anhängen, es funktioniert nur könnte (es tut in meinem Beispiel)

+0

dies eine gültige Antwort sein würde, wenn ich brauche 'mult' in C-Sprache zu codieren, aber ich brauche es in Python zu sein und verpackt Cmake Verwendung zusammen mit' add' und "subtrahieren" in C geschrieben ... danke – seninp

+0

Hinzugefügt etwas Adressierung dies mit 'pkgutil'. Ich habe diesen Teil in meinem ursprünglichen Beitrag irgendwie weggelassen. –

Verwandte Themen