NAPYR 2.0 - Not A Python Runtime REBOOTED
NAPYR is an acronym for "Not a Python runtime". I wrote the first version back in 2002 and it was a stillborn idea even then, but I am a fighter and not giving up, so here is version 2.0 :)
Motivation
I was reviewing talk proposals on Euro Python 14, and I saw that there were a several tasks on Python and C/C++ integration. There seem to be two mainstream ideas on how to go about that:
- Use cython
- Use boost.python
Neither of these options works for me:
- I have an existing C/C++ code base. I want to use Python to automate parts of it, but Python will not be "the" core product: it is the other way around
- When I see
../tools/boost/boost/mpi/detail/mpi_datatype_oarchive.hpp: In member function 'void boost::mpi::detail::mpi_datatype_oarchive::save_override(const T&, int) [with T = long unsigned int]':I reach for my gun. I hate bloated template messes. I am too old for this kind of shit. Things shouldn't be that complicated, not even in C++. I will write down my feelings about C++ templates some day, but today is not that day.
../tools/boost/boost/mpi/detail/mpi_datatype_oarchive.hpp:51: error: template instantiation depth exceeds maximum of 128 (use -ftemplate-depth-NN to increase the maximum) instantiating 'struct boost::is_enum<long unsigned int>'
Plus, a quick peek around this website will tell you that whatever p-nand-q.com is about, it is not "about mainstream programming ideas".
Introducing NAPYR 2.0
NAPYR 2.0 is designed to be small, fast, and no-nonsense integration of Python code in C/C++ code. It is easy to use. It doesn't produce tons of code. If you learned C++ 20 years ago and still do it, but like anyone sensible you use only the good bits and eschew monster templates, then this is for you. But it is freeware: if you don't like it, simply don't use it!
Typical Scenario
The typical NAPYR scenario is Python for embedded scripting:
- You have an existing C/C++ application and you want to embedd Python, so that parts of it become scriptable.
So the main flow looks like this:
- Your C/C++ application starts up
- Your application starts a Python interpreter that calls Python code in the background.
- That Python code is able to interact with your existing application
Things you do not need
- Write .PYDs. You have an existing infrastructure that you want to be called. If you write a PYD, then you have essentially another DLL that still needs to call your existing infrastructure.
- Write .PYDs in Cython by extension of (1).
- Drowning in compiler errors and build times that make a grown man weep
- setuptools, py2exe or the McMillan installer. Chances are that if you have an existing product, you have an existing setup infrastructure and you just want to extend that, rather than have a separate component with a separate toolchain.
- a compiler version that is several years outdated, or that doesn't interact with other technologies on Windows. Python as available is compiled with really old Microsoft Visual Studio versions, and if your application uses a newer version, you're in for a ride. And I mean come on guys, seriously, who is developing major business applications on Windows for Windows using cygwin?
Things you do need
- Support for multithreading / multicore programming.
- Code that you can debug easily
- Build infrastructure that allows you to package the python code
NAPYR makes all this really easy.
So how does it look?
- You need to include a single header,
napyr2.h, like this:#include "napyr2.h" - You create a
PythonRuntime. This is a wrapper object that represents the Python runtime. I know, this comes as quite a surprise for a component named Python Runtime, but there you go:
ornapyr2::PythonRuntime runtime;using namespace napyr2; ... PythonRuntime runtime; - You create a Python startup script. Here is a very simple script:
OK, maybe that was a little bit too simple, how about this:# Nothing to see here, move along
Agreed, the uninitated layperson may see this as a bit of oversimplification, but trust me, we're getting there... Here is a startup script that defines two functions that you'll later call from C:#! -*- Encoding: Latin-1 -*- print("Hello, World")
I think you get the idea. You write a script, that defines all the functions you want to be visible from C/C++. As you can see, you can include other modules as well, and you can split your code so that it is actually implemented in other python modules.import os def join_list(l, joiner): return joiner.join(l) def list_files(name): return os.listdir(name)