Demo Project ============ This walkthrough will take you through all the steps to create a simple robotpy-build project that autogenerates a working wrapper around a C++ class and it's methods. This demo should work on Linux, OSX, and Windows. Make sure you have robotpy_build installed first! .. note:: This demo shows building a python wrapper around C++ code that is self-contained in this project. However, ``robotpy-build`` also supports wrapping externally compiled libraries and inter-package shared library dependencies. Files + descriptions -------------------- .. note:: If you're lazy, the files for this demo are checked into the robotpy-build repository at ``examples/demo``. All of the content required for this demo is contained inline below. Let's start by creating a new directory for your project, and we're going to create the following files: setup.py ~~~~~~~~ Traditionally python projects required a setup.py that contained all the information and logic needed to compile/install the project. Every project that uses robotpy-build has an identical ``setup.py`` that looks like this: .. literalinclude:: ../examples/demo/setup.py pyproject.toml ~~~~~~~~~~~~~~ Projects that use robotpy-build must add a ``pyproject.toml`` to the root of their project as specified in `PEP 518 `_. This file is used to configure your project. Comments describing the function of each section can be found inline below. .. literalinclude:: ../examples/demo/pyproject.toml .. seealso:: For detailed information about the contents of ``pyproject.toml`` see :ref:`pyproject`. rpydemo/__init__.py ~~~~~~~~~~~~~~~~~~~ .. code-block:: python # file is empty for now rpydemo/src/demo.cpp ~~~~~~~~~~~~~~~~~~~~ This is the (very simple) C++ code that we will wrap so that it can be called from python. .. literalinclude:: ../examples/demo/rpydemo/src/demo.cpp rpydemo/include/demo.h ~~~~~~~~~~~~~~~~~~~~~~ This is the C++ header file for the code that we're wrapping. In ``pyproject.toml`` we told ``robotpy-build`` to parse this file and autogenerate wrappers for it. For simple C++ code such as this, autogeneration will 'just work' and no other customization is required. However, certain C++ code (templates and sometimes code that depends on templated types, and other complex circumstances) will require providing customization in a YAML file. .. literalinclude:: ../examples/demo/rpydemo/include/demo.h rpydemo/src/main.cpp ~~~~~~~~~~~~~~~~~~~~ Finally, you need to define your pybind11 python module. Custom ``pybind11`` projects would use a ``PYBIND11_MODULE`` macro to define a module, but it's easier to use the ``RPYBUILD_PYBIND11_MODULE`` macro which automatically sets the module name when robotpy-build compiles the file. .. literalinclude:: ../examples/demo/rpydemo/src/main.cpp .. note:: If you wanted to add your own handwritten pybind11 code here, you can add it in addition to the ``initWrapper`` call made here. See the pybind11 documentation for more details. Install the project ------------------- When developing a new project, it's easiest to just install in 'develop' mode which will build/install everything in the currect directory. .. code-block:: sh $ python3 setup.py develop .. note:: When using develop mode for robotpy-build projects, it is recommended to invoke setup.py directly instead of using ``pip3 install -e`` Adjust the project ------------------ As we've currently built the project, the CPython extension will be built as ``rpydemo._rpydemo``. For example: .. code-block:: pycon >>> from rpydemo._rpydemo import DemoClass >>> DemoClass While that works, we really would like users to be able to access our module directly by importing them into ``__init__.py``. There's a robotpy_build command for generating the contents of ``__init__.py``: .. code-block:: sh $ python -m robotpy_build create-imports rpydemo rpydemo._rpydemo The output from this you can put into your ``rpydemo/__init__.py``. It'll look like this: .. code-block:: python # autogenerated by 'robotpy-build create-imports rpydemo rpydemo._rpydemo' from ._rpydemo import DemoClass, add2 __all__ = ["DemoClass", "add2"] Now when we put this in our ``__init__.py``, that allows this to work instead: .. code-block:: pycon >>> from rpydemo import DemoClass >>> DemoClass Trying out the project ---------------------- Alright, now that all the pieces are assembled, we can try out our project: .. code-block:: pycon >>> import rpydemo >>> rpydemo.add2(2) 4 >>> d = rpydemo.DemoClass() >>> d.setX(2) >>> d.getX() 2 More Examples ------------- The integration tests in ``tests/cpp`` contains a python project that contains several autogenerated wrappers packages and various customizations.