bitbake: bitbake/pyinotify.py: Upgrade to py3 version

(Bitbake rev: 5ee80d77bc278758e411048ed09551ab65b9e72d)

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
Richard Purdie 2016-05-06 09:07:37 +01:00
parent 822eabf32d
commit deca147645
1 changed files with 101 additions and 155 deletions

View File

@ -42,13 +42,14 @@ class UnsupportedPythonVersionError(PyinotifyError):
@param version: Current Python version
@type version: string
"""
err = 'Python %s is unsupported, requires at least Python 2.4'
PyinotifyError.__init__(self, err % version)
PyinotifyError.__init__(self,
('Python %s is unsupported, requires '
'at least Python 3.0') % version)
# Check Python version
import sys
if sys.version_info < (2, 4):
if sys.version_info < (3, 0):
raise UnsupportedPythonVersionError(sys.version)
@ -68,6 +69,8 @@ from datetime import datetime, timedelta
import time
import re
import asyncore
import glob
import locale
import subprocess
try:
@ -75,12 +78,6 @@ try:
except ImportError:
pass # Will fail on Python 2.4 which has reduce() builtin anyway.
try:
from glob import iglob as glob
except ImportError:
# Python 2.4 does not have glob.iglob().
from glob import glob as glob
try:
import ctypes
import ctypes.util
@ -95,9 +92,7 @@ except ImportError:
__author__ = "seb@dbzteam.org (Sebastien Martini)"
__version__ = "0.9.5"
__metaclass__ = type # Use new-style classes by default
__version__ = "0.9.6"
# Compatibity mode: set to True to improve compatibility with
@ -122,6 +117,9 @@ class INotifyWrapper:
"""
@staticmethod
def create():
"""
Factory method instanciating and returning the right wrapper.
"""
# First, try to use ctypes.
if ctypes:
inotify = _CtypesLibcINotifyWrapper()
@ -173,7 +171,7 @@ class _INotifySyscallsWrapper(INotifyWrapper):
def _inotify_init(self):
try:
fd = inotify_syscalls.inotify_init()
except IOError, err:
except IOError as err:
self._last_errno = err.errno
return -1
return fd
@ -181,7 +179,7 @@ class _INotifySyscallsWrapper(INotifyWrapper):
def _inotify_add_watch(self, fd, pathname, mask):
try:
wd = inotify_syscalls.inotify_add_watch(fd, pathname, mask)
except IOError, err:
except IOError as err:
self._last_errno = err.errno
return -1
return wd
@ -189,7 +187,7 @@ class _INotifySyscallsWrapper(INotifyWrapper):
def _inotify_rm_watch(self, fd, wd):
try:
ret = inotify_syscalls.inotify_rm_watch(fd, wd)
except IOError, err:
except IOError as err:
self._last_errno = err.errno
return -1
return ret
@ -213,17 +211,8 @@ class _CtypesLibcINotifyWrapper(INotifyWrapper):
except (OSError, IOError):
pass # Will attemp to load it with None anyway.
if sys.version_info >= (2, 6):
self._libc = ctypes.CDLL(libc_name, use_errno=True)
self._get_errno_func = ctypes.get_errno
else:
self._libc = ctypes.CDLL(libc_name)
try:
location = self._libc.__errno_location
location.restype = ctypes.POINTER(ctypes.c_int)
self._get_errno_func = lambda: location().contents.value
except AttributeError:
pass
self._libc = ctypes.CDLL(libc_name, use_errno=True)
self._get_errno_func = ctypes.get_errno
# Eventually check that libc has needed inotify bindings.
if (not hasattr(self._libc, 'inotify_init') or
@ -241,9 +230,8 @@ class _CtypesLibcINotifyWrapper(INotifyWrapper):
return True
def _get_errno(self):
if self._get_errno_func is not None:
return self._get_errno_func()
return None
assert self._get_errno_func
return self._get_errno_func()
def _inotify_init(self):
assert self._libc is not None
@ -251,6 +239,11 @@ class _CtypesLibcINotifyWrapper(INotifyWrapper):
def _inotify_add_watch(self, fd, pathname, mask):
assert self._libc is not None
# Encodes path to a bytes string. This conversion seems required because
# ctypes.create_string_buffer seems to manipulate bytes internally.
# Moreover it seems that inotify_add_watch does not work very well when
# it receives an ctypes.create_unicode_buffer instance as argument.
pathname = pathname.encode(sys.getfilesystemencoding())
pathname = ctypes.create_string_buffer(pathname)
return self._libc.inotify_add_watch(fd, pathname, mask)
@ -258,10 +251,6 @@ class _CtypesLibcINotifyWrapper(INotifyWrapper):
assert self._libc is not None
return self._libc.inotify_rm_watch(fd, wd)
def _sysctl(self, *args):
assert self._libc is not None
return self._libc.sysctl(*args)
# Logging
def logger_init():
@ -278,97 +267,58 @@ log = logger_init()
# inotify's variables
class SysCtlINotify:
class ProcINotify:
"""
Access (read, write) inotify's variables through sysctl. Usually it
requires administrator rights to update them.
Access (read, write) inotify's variables through /proc/sys/. Note that
usually it requires administrator rights to update them.
Examples:
- Read max_queued_events attribute: myvar = max_queued_events.value
- Update max_queued_events attribute: max_queued_events.value = 42
"""
inotify_attrs = {'max_user_instances': 1,
'max_user_watches': 2,
'max_queued_events': 3}
def __init__(self, attrname, inotify_wrapper):
# FIXME: right now only supporting ctypes
assert ctypes
self._attrname = attrname
self._inotify_wrapper = inotify_wrapper
sino = ctypes.c_int * 3
self._attr = sino(5, 20, SysCtlINotify.inotify_attrs[attrname])
@staticmethod
def create(attrname):
"""
Factory method instanciating and returning the right wrapper.
"""
# FIXME: right now only supporting ctypes
if ctypes is None:
return None
inotify_wrapper = _CtypesLibcINotifyWrapper()
if not inotify_wrapper.init():
return None
return SysCtlINotify(attrname, inotify_wrapper)
def __init__(self, attr):
self._base = "/proc/sys/fs/inotify"
self._attr = attr
def get_val(self):
"""
Gets attribute's value. Raises OSError if the operation failed.
Gets attribute's value.
@return: stored value.
@rtype: int
@raise IOError: if corresponding file in /proc/sys cannot be read.
"""
oldv = ctypes.c_int(0)
size = ctypes.c_int(ctypes.sizeof(oldv))
sysctl = self._inotify_wrapper._sysctl
res = sysctl(self._attr, 3,
ctypes.c_voidp(ctypes.addressof(oldv)),
ctypes.addressof(size),
None, 0)
if res == -1:
raise OSError(self._inotify_wrapper.get_errno(),
self._inotify_wrapper.str_errno())
return oldv.value
with open(os.path.join(self._base, self._attr), 'r') as file_obj:
return int(file_obj.readline())
def set_val(self, nval):
"""
Sets new attribute's value. Raises OSError if the operation failed.
Sets new attribute's value.
@param nval: replaces current value by nval.
@type nval: int
@raise IOError: if corresponding file in /proc/sys cannot be written.
"""
oldv = ctypes.c_int(0)
sizeo = ctypes.c_int(ctypes.sizeof(oldv))
newv = ctypes.c_int(nval)
sizen = ctypes.c_int(ctypes.sizeof(newv))
sysctl = self._inotify_wrapper._sysctl
res = sysctl(self._attr, 3,
ctypes.c_voidp(ctypes.addressof(oldv)),
ctypes.addressof(sizeo),
ctypes.c_voidp(ctypes.addressof(newv)),
sizen)
if res == -1:
raise OSError(self._inotify_wrapper.get_errno(),
self._inotify_wrapper.str_errno())
with open(os.path.join(self._base, self._attr), 'w') as file_obj:
file_obj.write(str(nval) + '\n')
value = property(get_val, set_val)
def __repr__(self):
return '<%s=%d>' % (self._attrname, self.get_val())
return '<%s=%d>' % (self._attr, self.get_val())
# Inotify's variables
#
# FIXME: currently these variables are only accessible when ctypes is used,
# otherwise there are set to None.
# Note: may raise IOError if the corresponding value in /proc/sys
# cannot be accessed.
#
# read: myvar = max_queued_events.value
# update: max_queued_events.value = 42
# Examples:
# - read: myvar = max_queued_events.value
# - update: max_queued_events.value = 42
#
for attrname in ('max_queued_events', 'max_user_instances', 'max_user_watches'):
globals()[attrname] = SysCtlINotify.create(attrname)
globals()[attrname] = ProcINotify(attrname)
class EventsCodes:
@ -536,7 +486,7 @@ class _Event:
continue
if attr == 'mask':
value = hex(getattr(self, attr))
elif isinstance(value, basestring) and not value:
elif isinstance(value, str) and not value:
value = "''"
s += ' %s%s%s' % (output_format.field_name(attr),
output_format.punctuation('='),
@ -628,7 +578,7 @@ class Event(_Event):
self.name))
else:
self.pathname = os.path.abspath(self.path)
except AttributeError, err:
except AttributeError as err:
# Usually it is not an error some events are perfectly valids
# despite the lack of these attributes.
log.debug(err)
@ -718,8 +668,8 @@ class _SysProcessEvent(_ProcessEvent):
and self._mv.
"""
date_cur_ = datetime.now()
for seq in [self._mv_cookie, self._mv]:
for k in seq.keys():
for seq in (self._mv_cookie, self._mv):
for k in list(seq.keys()):
if (date_cur_ - seq[k][1]) > timedelta(minutes=1):
log.debug('Cleanup: deleting entry %s', seq[k][0])
del seq[k]
@ -767,9 +717,9 @@ class _SysProcessEvent(_ProcessEvent):
continue
rawevent = _RawEvent(created_dir_wd, flags, 0, name)
self._notifier.append_event(rawevent)
except OSError, err:
msg = "process_IN_CREATE, invalid directory %s: %s"
log.debug(msg % (created_dir, str(err)))
except OSError as err:
msg = "process_IN_CREATE, invalid directory: %s"
log.debug(msg % str(err))
return self.process_default(raw_event)
def process_IN_MOVED_FROM(self, raw_event):
@ -1097,8 +1047,8 @@ class Stats(ProcessEvent):
@type filename: string
"""
flags = os.O_WRONLY|os.O_CREAT|os.O_NOFOLLOW|os.O_EXCL
fd = os.open(filename, flags, 0600)
os.write(fd, str(self))
fd = os.open(filename, flags, 0o0600)
os.write(fd, bytes(self.__str__(), locale.getpreferredencoding()))
os.close(fd)
def __str__(self, scale=45):
@ -1107,7 +1057,7 @@ class Stats(ProcessEvent):
return ''
m = max(stats.values())
unity = float(scale) / m
unity = scale / m
fmt = '%%-26s%%-%ds%%s' % (len(output_format.field_value('@' * scale))
+ 1)
def func(x):
@ -1149,7 +1099,7 @@ class Notifier:
@type default_proc_fun: instance of ProcessEvent
@param read_freq: if read_freq == 0, events are read asap,
if read_freq is > 0, this thread sleeps
max(0, read_freq - timeout) seconds. But if
max(0, read_freq - (timeout / 1000)) seconds. But if
timeout is None it may be different because
poll is blocking waiting for something to read.
@type read_freq: int
@ -1161,8 +1111,9 @@ class Notifier:
until the amount of events to read is >= threshold.
At least with read_freq set you might sleep.
@type threshold: int
@param timeout:
https://docs.python.org/3/library/select.html#polling-objects
@param timeout: see read_freq above. If provided, it must be set in
milliseconds. See
https://docs.python.org/3/library/select.html#select.poll.poll
@type timeout: int
"""
# Watch Manager instance
@ -1228,7 +1179,8 @@ class Notifier:
milliseconds.
@param timeout: If specified it overrides the corresponding instance
attribute _timeout.
attribute _timeout. timeout must be sepcified in
milliseconds.
@type timeout: int
@return: New events to read.
@ -1240,8 +1192,8 @@ class Notifier:
if timeout is None:
timeout = self._timeout
ret = self._pollobj.poll(timeout)
except select.error, err:
if err[0] == errno.EINTR:
except select.error as err:
if err.args[0] == errno.EINTR:
continue # interrupted, retry
else:
raise
@ -1271,7 +1223,7 @@ class Notifier:
try:
# Read content from file
r = os.read(self._fd, queue_size)
except Exception, msg:
except Exception as msg:
raise NotifierError(msg)
log.debug('Event queue size: %d', queue_size)
rsum = 0 # counter
@ -1281,9 +1233,11 @@ class Notifier:
wd, mask, cookie, fname_len = struct.unpack('iIII',
r[rsum:rsum+s_size])
# Retrieve name
fname, = struct.unpack('%ds' % fname_len,
bname, = struct.unpack('%ds' % fname_len,
r[rsum + s_size:rsum + s_size + fname_len])
rawevent = _RawEvent(wd, mask, cookie, fname)
# FIXME: should we explictly call sys.getdefaultencoding() here ??
uname = bname.decode()
rawevent = _RawEvent(wd, mask, cookie, uname)
if self._coalesce:
# Only enqueue new (unique) events.
raweventstr = str(rawevent)
@ -1326,13 +1280,10 @@ class Notifier:
def __daemonize(self, pid_file=None, stdin=os.devnull, stdout=os.devnull,
stderr=os.devnull):
"""
@param pid_file: file where the pid will be written. If pid_file=None
the pid is written to
/var/run/<sys.argv[0]|pyinotify>.pid, if pid_file=False
no pid_file is written.
@param stdin:
@param stdout:
@param stderr: files associated to common streams.
pid_file: file where the pid will be written. If pid_file=None the pid
is written to /var/run/<sys.argv[0]|pyinotify>.pid, if
pid_file=False no pid_file is written.
stdin, stdout, stderr: files associated to common streams.
"""
if pid_file is None:
dirname = '/var/run/'
@ -1354,7 +1305,7 @@ class Notifier:
if (pid == 0):
# child
os.chdir('/')
os.umask(022)
os.umask(0o022)
else:
# parent 2
os._exit(0)
@ -1364,9 +1315,9 @@ class Notifier:
fd_inp = os.open(stdin, os.O_RDONLY)
os.dup2(fd_inp, 0)
fd_out = os.open(stdout, os.O_WRONLY|os.O_CREAT, 0600)
fd_out = os.open(stdout, os.O_WRONLY|os.O_CREAT, 0o0600)
os.dup2(fd_out, 1)
fd_err = os.open(stderr, os.O_WRONLY|os.O_CREAT, 0600)
fd_err = os.open(stderr, os.O_WRONLY|os.O_CREAT, 0o0600)
os.dup2(fd_err, 2)
# Detach task
@ -1375,8 +1326,9 @@ class Notifier:
# Write pid
if pid_file != False:
flags = os.O_WRONLY|os.O_CREAT|os.O_NOFOLLOW|os.O_EXCL
fd_pid = os.open(pid_file, flags, 0600)
os.write(fd_pid, str(os.getpid()) + '\n')
fd_pid = os.open(pid_file, flags, 0o0600)
os.write(fd_pid, bytes(str(os.getpid()) + '\n',
locale.getpreferredencoding()))
os.close(fd_pid)
# Register unlink function
atexit.register(lambda : os.unlink(pid_file))
@ -1441,9 +1393,12 @@ class Notifier:
Close inotify's instance (close its file descriptor).
It destroys all existing watches, pending events,...
This method is automatically called at the end of loop().
Afterward it is invalid to access this instance.
"""
self._pollobj.unregister(self._fd)
os.close(self._fd)
if self._fd is not None:
self._pollobj.unregister(self._fd)
os.close(self._fd)
self._fd = None
self._sys_proc_fun = None
@ -1468,7 +1423,7 @@ class ThreadedNotifier(threading.Thread, Notifier):
@type default_proc_fun: instance of ProcessEvent
@param read_freq: if read_freq == 0, events are read asap,
if read_freq is > 0, this thread sleeps
max(0, read_freq - timeout) seconds.
max(0, read_freq - (timeout / 1000)) seconds.
@type read_freq: int
@param threshold: File descriptor will be read only if the accumulated
size to read becomes >= threshold. If != 0, you likely
@ -1478,8 +1433,9 @@ class ThreadedNotifier(threading.Thread, Notifier):
until the amount of events to read is >= threshold. At
least with read_freq you might sleep.
@type threshold: int
@param timeout:
https://docs.python.org/3/library/select.html#polling-objects
@param timeout: see read_freq above. If provided, it must be set in
milliseconds. See
https://docs.python.org/3/library/select.html#select.poll.poll
@type timeout: int
"""
# Init threading base class
@ -1498,7 +1454,7 @@ class ThreadedNotifier(threading.Thread, Notifier):
Stop notifier's loop. Stop notification. Join the thread.
"""
self._stop_event.set()
os.write(self._pipe[1], 'stop')
os.write(self._pipe[1], b'stop')
threading.Thread.join(self)
Notifier.stop(self)
self._pollobj.unregister(self._pipe[0])
@ -1699,7 +1655,6 @@ class Watch:
class ExcludeFilter:
"""
ExcludeFilter is an exclusion filter.
"""
def __init__(self, arg_lst):
"""
@ -1731,16 +1686,13 @@ class ExcludeFilter:
def _load_patterns_from_file(self, filename):
lst = []
file_obj = file(filename, 'r')
try:
with open(filename, 'r') as file_obj:
for line in file_obj.readlines():
# Trim leading an trailing whitespaces
pattern = line.strip()
if not pattern or pattern.startswith('#'):
continue
lst.append(pattern)
finally:
file_obj.close()
return lst
def _match(self, regex, path):
@ -1764,7 +1716,6 @@ class WatchManagerError(Exception):
"""
WatchManager Exception. Raised on error encountered on watches
operations.
"""
def __init__(self, msg, wmd):
"""
@ -1851,7 +1802,7 @@ class WatchManager:
"""
try:
del self._wmd[wd]
except KeyError, err:
except KeyError as err:
log.error('Cannot delete unknown watch descriptor %s' % str(err))
@property
@ -1868,13 +1819,7 @@ class WatchManager:
"""
Format path to its internal (stored in watch manager) representation.
"""
# Unicode strings are converted back to strings, because it seems
# that inotify_add_watch from ctypes does not work well when
# it receives an ctypes.create_unicode_buffer instance as argument.
# Therefore even wd are indexed with bytes string and not with
# unicode paths.
if isinstance(path, unicode):
path = path.encode(sys.getfilesystemencoding())
# path must be a unicode string (str) and is just normalized.
return os.path.normpath(path)
def __add_watch(self, path, mask, proc_fun, auto_add, exclude_filter):
@ -1890,13 +1835,14 @@ class WatchManager:
return wd
watch = Watch(wd=wd, path=path, mask=mask, proc_fun=proc_fun,
auto_add=auto_add, exclude_filter=exclude_filter)
# wd are _always_ indexed with their original unicode paths in wmd.
self._wmd[wd] = watch
log.debug('New %s', watch)
return wd
def __glob(self, path, do_glob):
if do_glob:
return glob(path)
return glob.iglob(path)
else:
return [path]
@ -1907,11 +1853,8 @@ class WatchManager:
Add watch(s) on the provided |path|(s) with associated |mask| flag
value and optionally with a processing |proc_fun| function and
recursive flag |rec| set to True.
Ideally |path| components should not be unicode objects. Note that
although unicode paths are accepted there are converted to byte
strings before a watch is put on that path. The encoding used for
converting the unicode object is given by sys.getfilesystemencoding().
If |path| si already watched it is ignored, but if it is called with
All |path| components _must_ be str (i.e. unicode) objects.
If |path| is already watched it is ignored, but if it is called with
option rec=True a watch is put on each one of its not-watched
subdirectory.
@ -1945,10 +1888,9 @@ class WatchManager:
the class' constructor.
@type exclude_filter: callable object
@return: dict of paths associated to watch descriptors. A wd value
is positive if the watch was added sucessfully,
otherwise the value is negative. If the path was invalid
or was already watched it is not included into this returned
dictionary.
is positive if the watch was added sucessfully, otherwise
the value is negative. If the path was invalid or was already
watched it is not included into this returned dictionary.
@rtype: dict of {str: int}
"""
ret_ = {} # return {path: wd, ...}
@ -1958,6 +1900,11 @@ class WatchManager:
# normalize args as list elements
for npath in self.__format_param(path):
# Require that path be a unicode string
if not isinstance(npath, str):
ret_[path] = -3
continue
# unix pathname pattern expansion
for apath in self.__glob(npath, do_glob):
# recursively list subdirs according to rec param
@ -2242,7 +2189,6 @@ class WatchManager:
"Make watch manager ignoring new events.")
class RawOutputFormat:
"""
Format string representations.