Skip to content

Instantly share code, notes, and snippets.

@hetsch
Created June 12, 2012 14:23
Show Gist options
  • Save hetsch/2917841 to your computer and use it in GitHub Desktop.
Save hetsch/2917841 to your computer and use it in GitHub Desktop.
Inotifyx Python3 bindings
/*
* Copyright (c) 2004 Novell, Inc.
* Copyright (c) 2005 Manuel Amador <[email protected]>
* Copyright (c) 2009-2011 Forest Bond <[email protected]>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <Python.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/inotify.h>
#include <linux/types.h>
#include <linux/limits.h>
#define MAX_PENDING_PAUSE_COUNT 5
#define PENDING_PAUSE_MICROSECONDS 2000
#define PENDING_THRESHOLD(qsize) ((qsize) >> 1)
#define EVENT_SIZE (sizeof (struct inotify_event))
#define BUF_LEN (1024 * (EVENT_SIZE + 16))
static PyObject * inotifyx_init(PyObject *self, PyObject *args) {
int fd;
Py_BEGIN_ALLOW_THREADS;
fd = inotify_init();
Py_END_ALLOW_THREADS;
if(fd < 0) {
PyErr_SetFromErrno(PyExc_IOError);
return NULL;
}
return Py_BuildValue("i", fd);
}
static PyObject * inotifyx_add_watch(PyObject *self, PyObject *args) {
int fd;
char *path;
int watch_descriptor;
uint32_t mask;
mask = IN_ALL_EVENTS;
// changed from python2 (s) to python3 (u)
if(! PyArg_ParseTuple(args, "is|i", &fd, &path, &mask)) {
return NULL;
}
Py_BEGIN_ALLOW_THREADS;
watch_descriptor = inotify_add_watch(fd, (const char *)path, mask);
Py_END_ALLOW_THREADS;
if(watch_descriptor < 0) {
PyErr_SetFromErrno(PyExc_IOError);
return NULL;
}
return Py_BuildValue("i", watch_descriptor);
}
static PyObject * inotifyx_rm_watch(PyObject *self, PyObject *args) {
int fd;
int watch_descriptor;
int retvalue;
if(! PyArg_ParseTuple(args, "ii", &fd, &watch_descriptor)) {
return NULL;
}
Py_BEGIN_ALLOW_THREADS;
retvalue = inotify_rm_watch(fd, watch_descriptor);
Py_END_ALLOW_THREADS;
if(retvalue < 0) {
PyErr_SetFromErrno(PyExc_IOError);
return NULL;
}
return Py_BuildValue("i", retvalue);
}
static PyObject * inotifyx_get_events(PyObject *self, PyObject *args) {
int fd;
static char buf[BUF_LEN];
int i;
int len;
float timeout_arg;
struct timeval timeout;
void *ptimeout;
struct inotify_event *event;
fd_set read_fds;
int select_retval;
timeout_arg = -1.0;
if(! PyArg_ParseTuple(args, "i|f", &fd, &timeout_arg)) {
return NULL;
}
if(timeout_arg < 0.0) {
ptimeout = NULL;
} else {
timeout.tv_sec = (int)timeout_arg;
timeout.tv_usec = (int)(1000000.0 * (timeout_arg - (float)((int) timeout_arg)));
ptimeout = &timeout;
}
FD_ZERO(&read_fds);
FD_SET(fd, &read_fds);
Py_BEGIN_ALLOW_THREADS;
select_retval = select(fd + 1, &read_fds, NULL, NULL, ptimeout);
Py_END_ALLOW_THREADS;
if(select_retval == 0) {
// Timed out.
return PyList_New(0);
} else if(select_retval < 0) {
PyErr_SetFromErrno(PyExc_IOError);
return NULL;
}
PyObject* retvalue = PyList_New(0);
timeout.tv_sec = 0;
timeout.tv_usec = 0;
ptimeout = &timeout;
while (1) {
Py_BEGIN_ALLOW_THREADS;
len = read(fd, buf, BUF_LEN);
Py_END_ALLOW_THREADS;
if(len < 0) {
PyErr_SetFromErrno(PyExc_IOError);
return NULL;
} else if(len == 0) {
PyErr_SetString(PyExc_IOError, "event buffer too small");
return NULL;
}
i = 0;
while(i < len) {
event = (struct inotify_event *)(& buf[i]);
PyObject* value = NULL;
if(event->name[0] != '\0') {
value = Py_BuildValue(
// changed from python2 (s) to python3 (u)
"iiis",
event->wd,
event->mask,
event->cookie,
event->name
);
} else {
value = Py_BuildValue(
"iiiO",
event->wd,
event->mask,
event->cookie,
Py_None
);
}
if(PyList_Append(retvalue, value) == -1) {
return NULL;
}
i += EVENT_SIZE + event->len;
}
FD_ZERO(&read_fds);
FD_SET(fd, &read_fds);
Py_BEGIN_ALLOW_THREADS;
select_retval = select(fd + 1, &read_fds, NULL, NULL, ptimeout);
Py_END_ALLOW_THREADS;
if (select_retval <= 0)
break;
}
return retvalue;
}
static PyMethodDef InotifyMethods[] = {
{
"init",
inotifyx_init,
METH_VARARGS,
(
"init()\n\n"
"Initialize an inotify instance and return the associated file\n"
"descriptor. The file descriptor should be closed via os.close\n"
"after it is no longer needed."
)
},
{
"add_watch",
inotifyx_add_watch,
METH_VARARGS,
(
"add_watch(fd, path[, mask])\n\n"
"Add a watch for path and return the watch descriptor.\n"
"fd should be the file descriptor returned by init.\n"
"If left unspecified, mask defaults to IN_ALL_EVENTS.\n"
"See the inotify documentation for details."
)
},
{
"rm_watch",
inotifyx_rm_watch,
METH_VARARGS,
(
"rm_watch(fd, wd)\n\n"
"Remove the watch associated with watch descriptor wd.\n"
"fd should be the file descriptor returned by init.\n"
)
},
{
"get_events",
inotifyx_get_events,
METH_VARARGS,
"get_events(fd[, timeout])\n\n"
"Read events from inotify and return a list of tuples "
"(wd, mask, cookie, name).\n"
"The name field is None if no name is associated with the inotify event.\n"
"Timeout specifies a timeout in seconds (as an integer or float).\n"
"If left unspecified, there is no timeout and get_events will block\n"
"indefinitely. If timeout is zero, get_events will not block."
},
{NULL, NULL, 0, NULL}
};
/*
http://python3porting.com/cextensions.html
http://www.tutorialspoint.com/python/python_further_extensions.htm
*/
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"inotifyx.binding",
(
"Low-level interface to inotify. Do not use this module directly.\n"
"Instead, use the inotifyx module."
),
-1,
InotifyMethods,
NULL,
NULL,
NULL,
NULL
};
static PyObject * moduleinit(void) {
PyObject *module = PyModule_Create(&moduledef);
/*
PyObject* module = Py_InitModule3(
"inotifyx.binding",
InotifyMethods,
(
"Low-level interface to inotify. Do not use this module directly.\n"
"Instead, use the inotifyx module."
)
);
*/
if (module == NULL)
return NULL;
PyModule_AddIntConstant(module, "IN_ACCESS", IN_ACCESS);
PyModule_AddIntConstant(module, "IN_MODIFY", IN_MODIFY);
PyModule_AddIntConstant(module, "IN_ATTRIB", IN_ATTRIB);
PyModule_AddIntConstant(module, "IN_CLOSE_WRITE", IN_CLOSE_WRITE);
PyModule_AddIntConstant(module, "IN_CLOSE_NOWRITE", IN_CLOSE_NOWRITE);
PyModule_AddIntConstant(module, "IN_CLOSE", IN_CLOSE);
PyModule_AddIntConstant(module, "IN_OPEN", IN_OPEN);
PyModule_AddIntConstant(module, "IN_MOVED_FROM", IN_MOVED_FROM);
PyModule_AddIntConstant(module, "IN_MOVED_TO", IN_MOVED_TO);
PyModule_AddIntConstant(module, "IN_MOVE", IN_MOVE);
PyModule_AddIntConstant(module, "IN_CREATE", IN_CREATE);
PyModule_AddIntConstant(module, "IN_DELETE", IN_DELETE);
PyModule_AddIntConstant(module, "IN_DELETE_SELF", IN_DELETE_SELF);
PyModule_AddIntConstant(module, "IN_MOVE_SELF", IN_MOVE_SELF);
PyModule_AddIntConstant(module, "IN_UNMOUNT", IN_UNMOUNT);
PyModule_AddIntConstant(module, "IN_Q_OVERFLOW", IN_Q_OVERFLOW);
PyModule_AddIntConstant(module, "IN_IGNORED", IN_IGNORED);
PyModule_AddIntConstant(module, "IN_ONLYDIR", IN_ONLYDIR);
PyModule_AddIntConstant(module, "IN_DONT_FOLLOW", IN_DONT_FOLLOW);
PyModule_AddIntConstant(module, "IN_MASK_ADD", IN_MASK_ADD);
PyModule_AddIntConstant(module, "IN_ISDIR", IN_ISDIR);
PyModule_AddIntConstant(module, "IN_ONESHOT", IN_ONESHOT);
PyModule_AddIntConstant(module, "IN_ALL_EVENTS", IN_ALL_EVENTS);
return module;
}
PyMODINIT_FUNC
PyInit_binding(void) {
return moduleinit();
}
@hetsch
Copy link
Author

hetsch commented Jun 12, 2012

Symlinking built and installed so file
ln -s binding.cpython-32mu.so binding.so
seems to work.

But still not happy, have to fully understand http://www.python.org/dev/peps/pep-3149/ first.

@hetsch
Copy link
Author

hetsch commented Jun 15, 2012

Also had to change all s to u (PyBuildValue) to respect python3s new string/byte types
-- reverted back to (s) - u is broken

@hetsch
Copy link
Author

hetsch commented Jun 15, 2012

Always got that kind of error:

2012-06-15 15:07:37,246 BACKEND.FILESYSTEM.SCANNER.LINUX Unknown error while handling filesystem change
2012-06-15 15:07:37,246 BACKEND.FILESYSTEM.SCANNER.LINUX ../Objects/listobject.c:290: bad argument to internal function
Traceback (most recent call last):
File "linux.py", line 397, in run
for event in inotifyx.get_events(self.inotify_fd, 0):
File "/home/hetsch/.virtualenvs/medialibrary-python-3/lib/python3.2/site-packages/inotifyx/init.py", line 118, in get_events
for wd, mask, cookie, name in binding.get_events(fd, *args)
SystemError: ../Objects/listobject.c:290: bad argument to internal function

Has something to to with .app (MacOsX fileextensions) and inotify. In Ubuntu .app extensions show up as directory, so inotify tries to add a watch on the inner files of the .app file and crashes

@okaresz
Copy link

okaresz commented Mar 6, 2017

Since then, there are more recent patches for python3 compatibility on launchpad: https://bugs.launchpad.net/inotifyx/+bug/1006053

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment