aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/Twisted/py2/twisted/internet/inotify.py
diff options
context:
space:
mode:
authorshmel1k <shmel1k@ydb.tech>2023-11-26 18:16:14 +0300
committershmel1k <shmel1k@ydb.tech>2023-11-26 18:43:30 +0300
commitb8cf9e88f4c5c64d9406af533d8948deb050d695 (patch)
tree218eb61fb3c3b96ec08b4d8cdfef383104a87d63 /contrib/python/Twisted/py2/twisted/internet/inotify.py
parent523f645a83a0ec97a0332dbc3863bb354c92a328 (diff)
downloadydb-b8cf9e88f4c5c64d9406af533d8948deb050d695.tar.gz
add kikimr_configure
Diffstat (limited to 'contrib/python/Twisted/py2/twisted/internet/inotify.py')
-rw-r--r--contrib/python/Twisted/py2/twisted/internet/inotify.py419
1 files changed, 419 insertions, 0 deletions
diff --git a/contrib/python/Twisted/py2/twisted/internet/inotify.py b/contrib/python/Twisted/py2/twisted/internet/inotify.py
new file mode 100644
index 0000000000..23d8e8285c
--- /dev/null
+++ b/contrib/python/Twisted/py2/twisted/internet/inotify.py
@@ -0,0 +1,419 @@
+# -*- test-case-name: twisted.internet.test.test_inotify -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+This module provides support for Twisted to linux inotify API.
+
+In order to use this support, simply do the following (and start a reactor
+at some point)::
+
+ from twisted.internet import inotify
+ from twisted.python import filepath
+
+ def notify(ignored, filepath, mask):
+ \"""
+ For historical reasons, an opaque handle is passed as first
+ parameter. This object should never be used.
+
+ @param filepath: FilePath on which the event happened.
+ @param mask: inotify event as hexadecimal masks
+ \"""
+ print("event %s on %s" % (
+ ', '.join(inotify.humanReadableMask(mask)), filepath))
+
+ notifier = inotify.INotify()
+ notifier.startReading()
+ notifier.watch(filepath.FilePath("/some/directory"), callbacks=[notify])
+ notifier.watch(filepath.FilePath(b"/some/directory2"), callbacks=[notify])
+
+Note that in the above example, a L{FilePath} which is a L{bytes} path name
+or L{str} path name may be used. However, no matter what type of
+L{FilePath} is passed to this module, internally the L{FilePath} is
+converted to L{bytes} according to L{sys.getfilesystemencoding}.
+For any L{FilePath} returned by this module, the caller is responsible for
+converting from a L{bytes} path name to a L{str} path name.
+
+@since: 10.1
+"""
+
+from __future__ import print_function
+
+import os
+import struct
+
+from twisted.internet import fdesc
+from twisted.internet.abstract import FileDescriptor
+from twisted.python import log, _inotify
+
+
+# from /usr/src/linux/include/linux/inotify.h
+
+IN_ACCESS = 0x00000001 # File was accessed
+IN_MODIFY = 0x00000002 # File was modified
+IN_ATTRIB = 0x00000004 # Metadata changed
+IN_CLOSE_WRITE = 0x00000008 # Writeable file was closed
+IN_CLOSE_NOWRITE = 0x00000010 # Unwriteable file closed
+IN_OPEN = 0x00000020 # File was opened
+IN_MOVED_FROM = 0x00000040 # File was moved from X
+IN_MOVED_TO = 0x00000080 # File was moved to Y
+IN_CREATE = 0x00000100 # Subfile was created
+IN_DELETE = 0x00000200 # Subfile was delete
+IN_DELETE_SELF = 0x00000400 # Self was deleted
+IN_MOVE_SELF = 0x00000800 # Self was moved
+IN_UNMOUNT = 0x00002000 # Backing fs was unmounted
+IN_Q_OVERFLOW = 0x00004000 # Event queued overflowed
+IN_IGNORED = 0x00008000 # File was ignored
+
+IN_ONLYDIR = 0x01000000 # only watch the path if it is a directory
+IN_DONT_FOLLOW = 0x02000000 # don't follow a sym link
+IN_MASK_ADD = 0x20000000 # add to the mask of an already existing watch
+IN_ISDIR = 0x40000000 # event occurred against dir
+IN_ONESHOT = 0x80000000 # only send event once
+
+IN_CLOSE = IN_CLOSE_WRITE | IN_CLOSE_NOWRITE # closes
+IN_MOVED = IN_MOVED_FROM | IN_MOVED_TO # moves
+IN_CHANGED = IN_MODIFY | IN_ATTRIB # changes
+
+IN_WATCH_MASK = (IN_MODIFY | IN_ATTRIB |
+ IN_CREATE | IN_DELETE |
+ IN_DELETE_SELF | IN_MOVE_SELF |
+ IN_UNMOUNT | IN_MOVED_FROM | IN_MOVED_TO)
+
+
+_FLAG_TO_HUMAN = [
+ (IN_ACCESS, 'access'),
+ (IN_MODIFY, 'modify'),
+ (IN_ATTRIB, 'attrib'),
+ (IN_CLOSE_WRITE, 'close_write'),
+ (IN_CLOSE_NOWRITE, 'close_nowrite'),
+ (IN_OPEN, 'open'),
+ (IN_MOVED_FROM, 'moved_from'),
+ (IN_MOVED_TO, 'moved_to'),
+ (IN_CREATE, 'create'),
+ (IN_DELETE, 'delete'),
+ (IN_DELETE_SELF, 'delete_self'),
+ (IN_MOVE_SELF, 'move_self'),
+ (IN_UNMOUNT, 'unmount'),
+ (IN_Q_OVERFLOW, 'queue_overflow'),
+ (IN_IGNORED, 'ignored'),
+ (IN_ONLYDIR, 'only_dir'),
+ (IN_DONT_FOLLOW, 'dont_follow'),
+ (IN_MASK_ADD, 'mask_add'),
+ (IN_ISDIR, 'is_dir'),
+ (IN_ONESHOT, 'one_shot')
+]
+
+
+
+def humanReadableMask(mask):
+ """
+ Auxiliary function that converts a hexadecimal mask into a series
+ of human readable flags.
+ """
+ s = []
+ for k, v in _FLAG_TO_HUMAN:
+ if k & mask:
+ s.append(v)
+ return s
+
+
+
+class _Watch(object):
+ """
+ Watch object that represents a Watch point in the filesystem. The
+ user should let INotify to create these objects
+
+ @ivar path: The path over which this watch point is monitoring
+ @ivar mask: The events monitored by this watchpoint
+ @ivar autoAdd: Flag that determines whether this watch point
+ should automatically add created subdirectories
+ @ivar callbacks: L{list} of callback functions that will be called
+ when an event occurs on this watch.
+ """
+ def __init__(self, path, mask=IN_WATCH_MASK, autoAdd=False,
+ callbacks=None):
+ self.path = path.asBytesMode()
+ self.mask = mask
+ self.autoAdd = autoAdd
+ if callbacks is None:
+ callbacks = []
+ self.callbacks = callbacks
+
+
+ def _notify(self, filepath, events):
+ """
+ Callback function used by L{INotify} to dispatch an event.
+ """
+ filepath = filepath.asBytesMode()
+ for callback in self.callbacks:
+ callback(self, filepath, events)
+
+
+
+class INotify(FileDescriptor, object):
+ """
+ The INotify file descriptor, it basically does everything related
+ to INotify, from reading to notifying watch points.
+
+ @ivar _buffer: a L{bytes} containing the data read from the inotify fd.
+
+ @ivar _watchpoints: a L{dict} that maps from inotify watch ids to
+ watchpoints objects
+
+ @ivar _watchpaths: a L{dict} that maps from watched paths to the
+ inotify watch ids
+ """
+ _inotify = _inotify
+
+ def __init__(self, reactor=None):
+ FileDescriptor.__init__(self, reactor=reactor)
+
+ # Smart way to allow parametrization of libc so I can override
+ # it and test for the system errors.
+ self._fd = self._inotify.init()
+
+ fdesc.setNonBlocking(self._fd)
+ fdesc._setCloseOnExec(self._fd)
+
+ # The next 2 lines are needed to have self.loseConnection()
+ # to call connectionLost() on us. Since we already created the
+ # fd that talks to inotify we want to be notified even if we
+ # haven't yet started reading.
+ self.connected = 1
+ self._writeDisconnected = True
+
+ self._buffer = b''
+ self._watchpoints = {}
+ self._watchpaths = {}
+
+
+ def _addWatch(self, path, mask, autoAdd, callbacks):
+ """
+ Private helper that abstracts the use of ctypes.
+
+ Calls the internal inotify API and checks for any errors after the
+ call. If there's an error L{INotify._addWatch} can raise an
+ INotifyError. If there's no error it proceeds creating a watchpoint and
+ adding a watchpath for inverse lookup of the file descriptor from the
+ path.
+ """
+ path = path.asBytesMode()
+ wd = self._inotify.add(self._fd, path, mask)
+
+ iwp = _Watch(path, mask, autoAdd, callbacks)
+
+ self._watchpoints[wd] = iwp
+ self._watchpaths[path] = wd
+
+ return wd
+
+
+ def _rmWatch(self, wd):
+ """
+ Private helper that abstracts the use of ctypes.
+
+ Calls the internal inotify API to remove an fd from inotify then
+ removes the corresponding watchpoint from the internal mapping together
+ with the file descriptor from the watchpath.
+ """
+ self._inotify.remove(self._fd, wd)
+ iwp = self._watchpoints.pop(wd)
+ self._watchpaths.pop(iwp.path)
+
+
+ def connectionLost(self, reason):
+ """
+ Release the inotify file descriptor and do the necessary cleanup
+ """
+ FileDescriptor.connectionLost(self, reason)
+ if self._fd >= 0:
+ try:
+ os.close(self._fd)
+ except OSError as e:
+ log.err(e, "Couldn't close INotify file descriptor.")
+
+
+ def fileno(self):
+ """
+ Get the underlying file descriptor from this inotify observer.
+ Required by L{abstract.FileDescriptor} subclasses.
+ """
+ return self._fd
+
+
+ def doRead(self):
+ """
+ Read some data from the observed file descriptors
+ """
+ fdesc.readFromFD(self._fd, self._doRead)
+
+
+ def _doRead(self, in_):
+ """
+ Work on the data just read from the file descriptor.
+ """
+ self._buffer += in_
+ while len(self._buffer) >= 16:
+
+ wd, mask, cookie, size = struct.unpack("=LLLL", self._buffer[0:16])
+
+ if size:
+ name = self._buffer[16:16 + size].rstrip(b'\0')
+ else:
+ name = None
+
+ self._buffer = self._buffer[16 + size:]
+
+ try:
+ iwp = self._watchpoints[wd]
+ except KeyError:
+ continue
+
+ path = iwp.path.asBytesMode()
+ if name:
+ path = path.child(name)
+ iwp._notify(path, mask)
+
+ if (iwp.autoAdd and mask & IN_ISDIR and mask & IN_CREATE):
+ # mask & IN_ISDIR already guarantees that the path is a
+ # directory. There's no way you can get here without a
+ # directory anyway, so no point in checking for that again.
+ new_wd = self.watch(
+ path, mask=iwp.mask, autoAdd=True,
+ callbacks=iwp.callbacks
+ )
+ # This is very very very hacky and I'd rather not do this but
+ # we have no other alternative that is less hacky other than
+ # surrender. We use callLater because we don't want to have
+ # too many events waiting while we process these subdirs, we
+ # must always answer events as fast as possible or the overflow
+ # might come.
+ self.reactor.callLater(0,
+ self._addChildren, self._watchpoints[new_wd])
+ if mask & IN_DELETE_SELF:
+ self._rmWatch(wd)
+
+
+ def _addChildren(self, iwp):
+ """
+ This is a very private method, please don't even think about using it.
+
+ Note that this is a fricking hack... it's because we cannot be fast
+ enough in adding a watch to a directory and so we basically end up
+ getting here too late if some operations have already been going on in
+ the subdir, we basically need to catchup. This eventually ends up
+ meaning that we generate double events, your app must be resistant.
+ """
+ try:
+ listdir = iwp.path.children()
+ except OSError:
+ # Somebody or something (like a test) removed this directory while
+ # we were in the callLater(0...) waiting. It doesn't make sense to
+ # process it anymore
+ return
+
+ # note that it's true that listdir will only see the subdirs inside
+ # path at the moment of the call but path is monitored already so if
+ # something is created we will receive an event.
+ for f in listdir:
+ # It's a directory, watch it and then add its children
+ if f.isdir():
+ wd = self.watch(
+ f, mask=iwp.mask, autoAdd=True,
+ callbacks=iwp.callbacks
+ )
+ iwp._notify(f, IN_ISDIR|IN_CREATE)
+ # now f is watched, we can add its children the callLater is to
+ # avoid recursion
+ self.reactor.callLater(0,
+ self._addChildren, self._watchpoints[wd])
+
+ # It's a file and we notify it.
+ if f.isfile():
+ iwp._notify(f, IN_CREATE|IN_CLOSE_WRITE)
+
+
+ def watch(self, path, mask=IN_WATCH_MASK, autoAdd=False,
+ callbacks=None, recursive=False):
+ """
+ Watch the 'mask' events in given path. Can raise C{INotifyError} when
+ there's a problem while adding a directory.
+
+ @param path: The path needing monitoring
+ @type path: L{FilePath}
+
+ @param mask: The events that should be watched
+ @type mask: L{int}
+
+ @param autoAdd: if True automatically add newly created
+ subdirectories
+ @type autoAdd: L{bool}
+
+ @param callbacks: A list of callbacks that should be called
+ when an event happens in the given path.
+ The callback should accept 3 arguments:
+ (ignored, filepath, mask)
+ @type callbacks: L{list} of callables
+
+ @param recursive: Also add all the subdirectories in this path
+ @type recursive: L{bool}
+ """
+ if recursive:
+ # This behavior is needed to be compatible with the windows
+ # interface for filesystem changes:
+ # http://msdn.microsoft.com/en-us/library/aa365465(VS.85).aspx
+ # ReadDirectoryChangesW can do bWatchSubtree so it doesn't
+ # make sense to implement this at a higher abstraction
+ # level when other platforms support it already
+ for child in path.walk():
+ if child.isdir():
+ self.watch(child, mask, autoAdd, callbacks,
+ recursive=False)
+ else:
+ wd = self._isWatched(path)
+ if wd:
+ return wd
+
+ mask = mask | IN_DELETE_SELF # need this to remove the watch
+
+ return self._addWatch(path, mask, autoAdd, callbacks)
+
+
+ def ignore(self, path):
+ """
+ Remove the watch point monitoring the given path
+
+ @param path: The path that should be ignored
+ @type path: L{FilePath}
+ """
+ path = path.asBytesMode()
+ wd = self._isWatched(path)
+ if wd is None:
+ raise KeyError("%r is not watched" % (path,))
+ else:
+ self._rmWatch(wd)
+
+
+ def _isWatched(self, path):
+ """
+ Helper function that checks if the path is already monitored
+ and returns its watchdescriptor if so or None otherwise.
+
+ @param path: The path that should be checked
+ @type path: L{FilePath}
+ """
+ path = path.asBytesMode()
+ return self._watchpaths.get(path, None)
+
+
+INotifyError = _inotify.INotifyError
+
+
+__all__ = ["INotify", "humanReadableMask", "IN_WATCH_MASK", "IN_ACCESS",
+ "IN_MODIFY", "IN_ATTRIB", "IN_CLOSE_NOWRITE", "IN_CLOSE_WRITE",
+ "IN_OPEN", "IN_MOVED_FROM", "IN_MOVED_TO", "IN_CREATE",
+ "IN_DELETE", "IN_DELETE_SELF", "IN_MOVE_SELF", "IN_UNMOUNT",
+ "IN_Q_OVERFLOW", "IN_IGNORED", "IN_ONLYDIR", "IN_DONT_FOLLOW",
+ "IN_MASK_ADD", "IN_ISDIR", "IN_ONESHOT", "IN_CLOSE",
+ "IN_MOVED", "IN_CHANGED"]