Skip to content

Instantly share code, notes, and snippets.

@hkwi
Last active February 3, 2017 08:31
Show Gist options
  • Save hkwi/3034e3b98aa6d643e91e9a77fa2218bc to your computer and use it in GitHub Desktop.
Save hkwi/3034e3b98aa6d643e91e9a77fa2218bc to your computer and use it in GitHub Desktop.
inotify eventlet integration

Finally, I found eventlet-inotifyx.

import os
import errno
import ctypes.util
libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True)
IN_NONBLOCK = os.O_NONBLOCK
if hasattr(os, "O_CLOEXEC"): # >=python3.3
IN_CLOEXEC = os.O_CLOEXEC
class Event(ctypes.Structure):
name = None
_fields_ = [("wd", ctypes.c_int),
("mask", ctypes.c_uint32),
("cookie", ctypes.c_uint32),
("len", ctypes.c_uint32)]
def __repr__(self):
return "(wd=%d, mask=%s, cookie=%d, name=%s)" % (self.wd, mask_tostr(self.mask), self.cookie, self.name)
def parse_events(buf, rlen):
evs = []
off = 0
while off < rlen:
ev = Event.from_buffer(buf, off)
ev.name = buf[off+ctypes.sizeof(ev):][:ev.len]
off += ctypes.sizeof(ev) + ev.len
evs.append(ev)
return evs
_mask_flags = (
("IN_ACCESS" , 0x00000001),
("IN_MODIFY" , 0x00000002),
("IN_ATTRIB" , 0x00000004),
("IN_CLOSE_WRITE" , 0x00000008),
("IN_CLOSE_NOWRITE" , 0x00000010),
("IN_OPEN" , 0x00000020),
("IN_MOVED_FROM" , 0x00000040),
("IN_MOVED_TO" , 0x00000080),
("IN_CREATE" , 0x00000100),
("IN_DELETE" , 0x00000200),
("IN_DELETE_SELF" , 0x00000400),
("IN_MOVE_SELF" , 0x00000800),
("IN_UNMOUNT" , 0x00002000),
("IN_Q_OVERFLOW" , 0x00004000),
("IN_IGNORED" , 0x00008000),
("IN_ONLYDIR" , 0x01000000),
("IN_DONT_FOLLOW" , 0x02000000),
("IN_EXCL_UNLINK" , 0x04000000),
("IN_MASK_ADD" , 0x20000000),
("IN_ISDIR" , 0x40000000),
("IN_ONESHOT" , 0x80000000),
)
for k,v in _mask_flags:
globals()[k] = v
def mask_tostr(mask):
nm = []
for k,v in _mask_flags:
if v&mask:
nm.append(k)
return "|".join(nm)
_mask_names = (
("IN_CLOSE" , IN_CLOSE_WRITE | IN_CLOSE_NOWRITE),
("IN_MOVE" , IN_MOVED_FROM | IN_MOVED_TO),
("IN_ALL_EVENTS" , (IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE |
IN_CLOSE_NOWRITE | IN_OPEN | IN_MOVED_FROM |
IN_MOVED_TO | IN_DELETE | IN_CREATE | IN_DELETE_SELF |
IN_MOVE_SELF))
)
for k,v in _mask_names:
globals()[k] = v
def _check_fail(rc):
if rc == -1:
raise OSError(ctypes.get_errno())
return rc
for k in ("init","init1","add_watch","rm_watch"):
name = "inotify_"+k
func = getattr(libc, name)
func.restype = _check_fail
globals()[name] = func
def read(fd, nonblocking=False):
evs = []
n = 128
buf = ctypes.create_string_buffer(n)
while True:
if len(buf) != n:
buf = ctypes.create_string_buffer(n)
rlen = libc.read(fd, ctypes.pointer(buf), len(buf))
if not nonblocking:
if rlen == -1:
raise OSError(ctypes.get_errno())
else:
return parse_events(buf,rlen)
elif rlen == 0:
n *= 2
continue
elif rlen == -1:
err = ctypes.get_errno()
if err == errno.EINVAL:
n *= 2
continue
elif err == errno.EINTR:
continue
elif err == errno.EAGAIN:
break
else:
raise OSError(err)
else:
evs += parse_events(buf,rlen)
return evs
if __name__=="__main__":
fd=inotify_init()
print(inotify_add_watch(fd,"/home/vagrant",IN_ALL_EVENTS))
print(read(fd))
fd=inotify_init1(IN_NONBLOCK)
print(inotify_add_watch(fd,"/home/vagrant",IN_ALL_EVENTS))
import select
select.select([fd],[],[])
print(read(fd, nonblocking=True))
import eventlet.hubs
from inotify import *
class Inotify(object):
def __init__(self):
self.fd = inotify_init1(IN_NONBLOCK)
self.wds = dict()
hub = eventlet.hubs.get_hub()
def cb(fd):
assert fd == self.fd
for ev in read(fd): # TODO: After some real-world test, one callback invocation per one read looks better.
watch = self.wds.get(ev.wd, None)
if watch:
watch.callback(ev.mask, ev.cookie, ev.name)
eventlet.getcurrent().switch()
hub.add(hub.READ, self.fd, cb, eventlet.getcurrent().throw, lambda: None)
def watch(self, path, mask, callback):
'''
callback signature is: `callback(event_flags, cookie, filename)`
'''
wd = inotify_add_watch(self.fd, path, mask)
w = Watch(self, wd, callback)
self.wds[wd] = w
return w
class Watch(object):
def __init__(self, inotify, wd, callback):
self.inotify = inotify
self.wd = wd
self.callback = callback
def close(self):
inotify_rm_watch(self.inotify.fd, self.wd)
del(self.inotify.wds[self.wd])
if __name__=="__main__":
def hoge(mask, cookie, name):
print(mask_tostr(mask), cookie, name)
w = Inotify().watch(".", IN_ALL_EVENTS, hoge)
eventlet.sleep(30)
w.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment