(normally) - in two flavors. Used only to prioritize high-traffic pages for dev work, iff JS and tracker enabled. The former/defunct training site's id: ga('create', 'UA-52578333-1', 'auto'). The book site's id, now used for all: ga('create', 'UA-52579036-1', 'auto'). JS is also used if enabled for thumbspage galleries, Top buttons, TOCs, etc. Jun-2019: tell Google Analytics to anonymize IP address on receipt for privacy. More: https://developers.google.com/analytics/devguides/collection/analyticsjs/. Oct-2022: install a GA4 tag (i.e., script) alongside the prior UA tag. UA tags will stop collecting data in 2023. It's okay to use both until the cutover (and Google advises doing so), but the UA tag will be pointless after that and should be removed. This handles genhtml-insert clients; others get tags by running insert-analytics.py in package-publishing scripts. Also per Google: "In Google Analytics 4, IP anonymization is not necessary since IP addresses are not logged or stored". Let's hope so... -->
This page contains a few additional Python/C integration examples which didn't make it into the first edition, but will probably show up in the second in one form or another (and/or in other publications before that).
For instance, the chapter on embedding (15) may be split into two: one on built-in API tools, and another on the extended API developed in the current chapter 15. The extended API was conceived as a way to teach basic embedding, but I'd like to make it more distinct in the next edition.
Note: there are no plans to start the second edition yet (and it will probably be a year or more before we even talk about it!), so please take these only as supplemental examples, not a preview.
task (in C)
import module
object = module.klass()
result = object.method(..args..)
file: module.py
class klass:
def method(self, x, y):
return "brave %s %s" % (x, y) # run me from C
file: objects1.c
#include <Python.h>
#include <import.h>
#include <stdio.h>
char *Py_GetProgramName() { return "objects1"; }
#define error(msg) do { printf("%s\n", msg); exit(1); } while (1)
main() {
char *arg1="sir", *arg2="robin", *cstr;
PyObject *pmod, *pclass, *pargs, *pinst, *pmeth, *pres;
/* instance = module.klass() */
Py_Initialize();
pmod = PyImport_ImportModule("module"); /* fetch module */
if (pmod == NULL)
error("Can't load module");
pclass = PyObject_GetAttrString(pmod, "klass"); /* fetch module.class */
Py_DECREF(pmod);
if (pclass == NULL)
error("Can't get module.klass");
pargs = Py_BuildValue("()");
if (pargs == NULL) {
Py_DECREF(pclass);
error("Can't build arguments list");
}
pinst = PyEval_CallObject(pclass, pargs); /* call class() */
Py_DECREF(pclass);
Py_DECREF(pargs);
if (pinst == NULL)
error("Error calling module.klass()");
/* result = instance.method(x,y) */
pmeth = PyObject_GetAttrString(pinst, "method"); /* fetch bound method */
Py_DECREF(pinst);
if (pmeth == NULL)
error("Can't fetch klass.method");
pargs = Py_BuildValue("(ss)", arg1, arg2); /* convert to Python */
if (pargs == NULL) {
Py_DECREF(pmeth);
error("Can't build arguments list");
}
pres = PyEval_CallObject(pmeth, pargs); /* call method(x,y) */
Py_DECREF(pmeth);
Py_DECREF(pargs);
if (pres == NULL)
error("Error calling klass.method");
if (!PyArg_Parse(pres, "s", &cstr)) /* convert to C */
error("Can't convert klass.method result");
printf("%s\n", cstr);
Py_DECREF(pres);
}
% objects1
brave sir robin
Same task as above, but with the extended API in chapter 15.
file: objects2.c
#include <stdio.h>
#include "pyembed.h"
char *Py_GetProgramName() { return "objects2"; }
main () {
int failflag;
PyObject *pinst;
char *arg1="sir", *arg2="robin", *cstr;
failflag = Run_Function("module", "klass", "O", &pinst, "()") ||
Run_Method(pinst, "method", "s", &cstr, "(ss)", arg1, arg2);
printf("%s\n", (!failflag) ? cstr : "Can't call objects");
Py_XDECREF(pinst);
}
Run "upper('spam') + '!'" in string module's namespace.
file codestring1.c
#include <Python.h> /* standard API defs */
#include <import.h> /* PyImport functions */
#include <graminit.h> /* parse-mode flags */
#include <pythonrun.h> /* PyRun interfaces */
char *Py_GetProgramName() { return "codestring1"; }
void error(char *msg) { printf("%s\n", msg); exit(1); }
main() {
char *cstr;
PyObject *pstr, *pmod, *pdict; /* with error tests */
Py_Initialize();
/* result = string.upper('spam') + '!' */
pmod = PyImport_ImportModule("string"); /* fetch module */
if (pmod == NULL) /* for name-space */
error("Can't import module");
pdict = PyModule_GetDict(pmod); /* string.__dict__ */
Py_DECREF(pmod);
if (pdict == NULL)
error("Can't get module dict");
pstr = PyRun_String("upper('spam') + '!'", eval_input, pdict, pdict);
if (pstr == NULL)
error("Error while running string");
/* convert result to C */
if (!PyArg_Parse(pstr, "s", &cstr))
error("Bad result type");
printf("%s\n", cstr);
Py_DECREF(pstr); /* free exported objects, not pdict */
}
% codestring1
SPAM!
Same task, but using extended API in chapter 15
file: codestring2.c
#include "pyembed.h"
#include <stdio.h>
char *Py_GetProgramName() { return "codestring2"; }
main() {
char *cstr;
int err = Run_Codestr(
PY_EXPRESSION, /* expr or stmt? */
"upper('spam') + '!'", "string", /* code, module */
"s", &cstr); /* expr result */
printf("%s\n", (!err) ? cstr : "Can't run string");
}
Discussed abstractly near the end of chapter 15, but here's a concrete example. Note that this is just a way to locate embedded code to run; the code can be objects (as here), code strings, etc.
file: cregister.c
#include <Python.h>
#include <stdlib.h>
/***********************************************/
/* 1) code to route events to Python object */
/* note that we could run strings here instead */
/***********************************************/
static PyObject *Handler = NULL; /* keep Python object in C */
void Route_Event(char *label, int count)
{
char *cres;
PyObject *args, *pres;
/* call Python handler */
args = Py_BuildValue("(si)", label, count); /* make arg-list */
pres = PyEval_CallObject(Handler, args); /* apply: run a call */
Py_DECREF(args); /* add error checks */
if (pres != NULL) {
/* use and decref handler result */
PyArg_Parse(pres, "s", &cres);
printf("%s\n", cres);
Py_DECREF(pres);
}
}
/*****************************************************/
/* 2) python extension module to register handlers */
/* python imports this module to set handler objects */
/*****************************************************/
static PyObject *
Register_Handler(PyObject *self, PyObject *args)
{
/* save Python callable object */
Py_XDECREF(Handler); /* called before? */
PyArg_Parse(args, "O", &Handler); /* one argument? */
Py_XINCREF(Handler); /* add a reference */
Py_INCREF(Py_None); /* return 'None': success */
return Py_None;
}
static PyObject *
Trigger_Event(PyObject *self, PyObject *args)
{
/* let Python simulate event caught by C */
static count = 0;
Route_Event("spam", count++);
Py_INCREF(Py_None);
return Py_None;
}
static struct PyMethodDef cregister_methods[] = {
{"setHandler", Register_Handler}, /* name, address */
{"triggerEvent", Trigger_Event},
{NULL, NULL}
};
void initcregister() /* this is called by Python */
{ /* on first "import cregister" */
(void) Py_InitModule("cregister", cregister_methods);
}
file: register.py
# handle an event, return a result (or None)
def function1(label, count):
return "%s number %i..." % (label, count)
def function2(label, count):
return label * count
# register handlers, trigger events
import cregister
cregister.setHandler(function1)
for i in range(3):
cregister.triggerEvent() # simulate events caught by C layer
cregister.setHandler(function2)
for i in range(3):
cregister.triggerEvent() # routes these events to function2
% python register.py
spam number 0...
spam number 1...
spam number 2...
spamspamspam
spamspamspamspam
spamspamspamspamspam
Back to the errata page
Back to my homepage