Embed CPython Script

To run in OSv

cd pythia/examples
pythia cpython_embed.md --osv

The script below is marked with @embed which turns it into a static string in the final C++, and is run after cpython.initalize() is called.

@/myfolder/somefile.json

my-data

@embed


class A():
    def __init__(self):
        self.value = 100
    def pymethod(self):
        print 'from cpython: self.value=', self.value
        if hasattr(self, 'my_dynamic_var'):
            print self.my_dynamic_var

    def add(self, a,b):
        return a+b

def foo():
    print 'foo'
    print open('myfolder/somefile.json', 'r').read()
    return A()

OSv image manifest

Below is the minimal set of files required to run Python.

@usr.manifest

/usr/lib/python2.7/site.py: /usr/lib/python2.7/site.py
/usr/lib/python2.7/os.py: /usr/lib/python2.7/os.py
/usr/lib/python2.7/posixpath.py: /usr/lib/python2.7/posixpath.py
/usr/lib/python2.7/stat.py: /usr/lib/python2.7/stat.py
/usr/lib/python2.7/genericpath.py: /usr/lib/python2.7/genericpath.py
/usr/lib/python2.7/warnings.py: /usr/lib/python2.7/warnings.py
/usr/lib/python2.7/linecache.py: /usr/lib/python2.7/linecache.py
/usr/lib/python2.7/types.py: /usr/lib/python2.7/types.py
/usr/lib/python2.7/UserDict.py: /usr/lib/python2.7/UserDict.py
/usr/lib/python2.7/_abcoll.py: /usr/lib/python2.7/_abcoll.py
/usr/lib/python2.7/abc.py: /usr/lib/python2.7/abc.py
/usr/lib/python2.7/_weakrefset.py: /usr/lib/python2.7/_weakrefset.py
/usr/lib/python2.7/copy_reg.py: /usr/lib/python2.7/copy_reg.py
/usr/lib/python2.7/traceback.py: /usr/lib/python2.7/traceback.py
/usr/lib/python2.7/sysconfig.py: /usr/lib/python2.7/sysconfig.py
/usr/lib/python2.7/re.py: /usr/lib/python2.7/re.py
/usr/lib/python2.7/sre_compile.py: /usr/lib/python2.7/sre_compile.py
/usr/lib/python2.7/sre_parse.py: /usr/lib/python2.7/sre_parse.py
/usr/lib/python2.7/sre_constants.py: /usr/lib/python2.7/sre_constants.py
/usr/lib/python2.7/_sysconfigdata.py: /usr/lib/python2.7/_sysconfigdata.py
/usr/lib/python2.7/plat-x86_64-linux-gnu/_sysconfigdata_nd.py: /usr/lib/python2.7/plat-x86_64-linux-gnu/_sysconfigdata_nd.py

CPython CAPI

The code below shows how to use the cpython module that wraps around the CPython C-API. https://docs.python.org/2/c-api/object.html

Below a.value as int it is trival to know that a is a PyObject and that using the . operator on it makes Pythia generate the code that calls the CPython C-API. Translation to C++:

auto v = static_cast<int>(PyInt_AS_LONG(PyObject_GetAttrString(a,"value")));

The syntax is used -> to make it explicit that the object is a PyObject, and to make Pythia generate the required CPython C-API calls.
For example b->pymethod() becomes this C++:

PyObject_Call(
    PyObject_GetAttrString(b,"pymethod"),
    Py_BuildValue("()"),
    NULL
);

Build Options

  • @link:python2.7
  • @include:/usr/include/python2.7
#backend:c++
import cpython

def main():
    print readfile( open('myfolder/somefile.json','r'))
    print 'init cpython...'
    state = cpython.initalize()
    with gil:
        a = cpython.foo()
        print 'addr of a:', a
        print a..value as int
        a..pymethod()

        b = a
        b..pymethod()
        print b..value as int
        v = b..value as int
        print v + 400
        c = b..pymethod()

        r = b..add(1, 2) as int
        print r
        u = b..add(
            b..value, 
            b..value 
        ) as int
        print u

        if hasattr(b, "value"):
            print('builtin `hasattr` works on b')
            print str(b)
            b..value = 'set from c++' as pystring
            b..pymethod()
            b..my_dynamic_var = 'hello dynamic var' as pystring
            a..pymethod()
            setattr(a, 'my_dynamic_var', 'setattr OK' as pystring)
            a..pymethod()

            s = b..my_dynamic_var as string
            print s

        pyob = cpython.foo()
        b..my_dynamic_var = pyob
        b..pymethod()

    cpython.finalize(state)