Hooking __import__ when embedding the interpreter

S

Stefan Bellon

Hi all,

I am embedding the Python interpreter using the C API and extending it
using modules generated by SWIG. In order to guarantee consistency
about importing those modules, I would like to "hook" into the Python's
import statement and __import__ function and do some checks there.

I have experimented a bit already, but still have a few questions and
would be happy if anybody from the group could shed some light on those.

At present, I have the following test code:


#include <stdio.h>
#include <Python.h>

static PyObject *
__import__(PyObject *self, PyObject *args)
{
char *name;
PyObject *globals = NULL;
PyObject *locals = NULL;
PyObject *fromlist = NULL;

if (!PyArg_ParseTuple
(args, "s|OOO:__import__", &name, &globals, &locals, &fromlist))
{
return NULL;
}

PyObject *m = PyDict_GetItemString(PyImport_GetModuleDict(), name);
if (m)
{
Py_INCREF(m);
return m;
}

if (!strcmp(name, "foobar")) // to be replaced by the consistency check!
{
PyErr_SetString
(PyExc_ImportError, "cannot import module right now");
return NULL;
}

return PyImport_ImportModuleEx(name, globals, locals, fromlist);
}

static PyMethodDef import_hook[] =
{
{ "__import__", __import__ },
{ NULL, NULL }
};

int main()
{
Py_Initialize();

Py_InitModule("import_hook", import_hook);

PyObject_SetAttrString
(PyImport_ImportModule("__builtin__"),
"__import__",
PyObject_GetAttrString
(PyImport_ImportModule("import_hook"), "__import__"));

PyRun_InteractiveLoop(stdin, "<stdin>");

Py_Finalize();
}


The code of __import__ above was inspired by the code of
builtin__import__ in Python/bltinmodule.c with the addition of the
early exit if the module is already loaded and the "consistency"
check which I reduced to some "cannot load module foobar" for ease
of the test case.


Now to my open questions.

1) The above code seems to work ok when using the "import" statement,
but it does not when using the dynamic __import__ function. If using
it that way, I get:
Traceback (most recent call last):
File "<stdin>", line 1, in ?
SystemError: new style getargs format but argument is not a tuple

What am I missing in order to get the __import__ function covered
as well?


2) Another point is, that I need to check _from where_ the module is
imported. In fact, this is going to become part of the consistency
check condition. How can I find out from which module the import
was initiated?


3) My final point is related to 2) ... if I get to the module object,
then how do I get at the source file name of that? I noticed that
when a .pyc is available, then this is preferred. I'd like to get at
the .py file itself. Is this available or do I just have to strip
off the trailing 'c' (or 'o'?) if present (seems hacky to me).


I am very much looking forward to your suggestions!

Greetings,
Stefan
 
S

Stefan Bellon

I think I solved all my three questions for myself now ...

1) The above code seems to work ok when using the "import" statement,
but it does not when using the dynamic __import__ function. If
using it that way, I get:

Traceback (most recent call last):
File "<stdin>", line 1, in ?
SystemError: new style getargs format but argument is not a tuple

What am I missing in order to get the __import__ function covered
as well?

static PyMethodDef import_hook[] =
{
{"__import__", __import__, METH_VARARGS, import_doc},
{NULL, NULL}
};

Adding the METH_VARARGS solved this, now the __import__ function is
intercepted as well.
2) Another point is, that I need to check _from where_ the module is
imported. In fact, this is going to become part of the consistency
check condition. How can I find out from which module the import
was initiated?


3) My final point is related to 2) ... if I get to the module object,
then how do I get at the source file name of that? I noticed that
when a .pyc is available, then this is preferred. I'd like to get
at the .py file itself. Is this available or do I just have to
strip off the trailing 'c' (or 'o'?) if present (seems hacky to
me).

By not focusing on the module but using
sys._getframe().f_code.co_filename I solved point 2 and 3 in one go.

I'm still looking forward to comments regarding this issue. ;-)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,059
Latest member
cryptoseoagencies

Latest Threads

Top