<s> """<STR_LIT>""" <EOL> import ctypes <EOL> import ctypes . util <EOL> import errno <EOL> import itertools <EOL> import logging <EOL> import os <EOL> import select <EOL> import struct <EOL> import sys <EOL> import threading <EOL> from google . appengine . tools . devappserver2 import watcher_common <EOL> IN_MODIFY = <NUM_LIT> <EOL> IN_ATTRIB = <NUM_LIT> <EOL> IN_MOVED_FROM = <NUM_LIT> <EOL> IN_MOVED_TO = <NUM_LIT> <EOL> IN_CREATE = <NUM_LIT> <EOL> IN_DELETE = <NUM_LIT> <EOL> IN_IGNORED = <NUM_LIT> <EOL> IN_ISDIR = <NUM_LIT> <EOL> _INOTIFY_EVENT = struct . Struct ( '<STR_LIT>' ) <EOL> _INOTIFY_EVENT_SIZE = _INOTIFY_EVENT . size <EOL> _INTERESTING_INOTIFY_EVENTS = ( <EOL> IN_ATTRIB | IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE | IN_DELETE ) <EOL> if sys . platform . startswith ( '<STR_LIT>' ) : <EOL> _libc = ctypes . CDLL ( ctypes . util . find_library ( '<STR_LIT:c>' ) , use_errno = True ) <EOL> _libc . inotify_init . argtypes = [ ] <EOL> _libc . inotify_init . restype = ctypes . c_int <EOL> _libc . inotify_add_watch . argtypes = [ ctypes . c_int , <EOL> ctypes . c_char_p , <EOL> ctypes . c_uint32 ] <EOL> _libc . inotify_add_watch . restype = ctypes . c_int <EOL> _libc . inotify_rm_watch . argtypes = [ ctypes . c_int , <EOL> ctypes . c_int ] <EOL> _libc . inotify_rm_watch . restype = ctypes . c_int <EOL> else : <EOL> _libc = None <EOL> _ATTRIBUTE_NAMES = [ <EOL> '<STR_LIT>' , <EOL> '<STR_LIT>' , <EOL> '<STR_LIT>' , <EOL> '<STR_LIT>' , <EOL> '<STR_LIT>' , <EOL> '<STR_LIT>' , <EOL> '<STR_LIT>' , <EOL> '<STR_LIT>' , <EOL> ] <EOL> _ATTRIBUTE_MASK_NAMES = { globals ( ) [ name ] : name for name in _ATTRIBUTE_NAMES } <EOL> _AGGREGATE_CHANGES_MS_APART = <NUM_LIT:100> <EOL> def _bit_str ( bits , mask_names ) : <EOL> """<STR_LIT>""" <EOL> hex_str = hex ( bits ) <EOL> names = [ ] <EOL> mask = <NUM_LIT> <EOL> while bits : <EOL> if bits & mask : <EOL> bits &= ~ mask <EOL> names . append ( mask_names . get ( mask , '<STR_LIT>' % mask ) ) <EOL> mask <<= <NUM_LIT:1> <EOL> return '<STR_LIT>' % ( '<STR_LIT:|>' . join ( names ) , hex_str ) <EOL> class InotifyFileWatcher ( object ) : <EOL> """<STR_LIT>""" <EOL> SUPPORTS_MULTIPLE_DIRECTORIES = True <EOL> def __init__ ( self , directories ) : <EOL> """<STR_LIT>""" <EOL> assert _libc is not None , '<STR_LIT>' <EOL> self . _directories = [ os . path . abspath ( d ) for d in directories ] <EOL> self . _real_directories = [ os . path . realpath ( d ) for d in self . _directories ] <EOL> self . _watch_to_directory = { } <EOL> self . _directory_to_watch_descriptor = { } <EOL> self . _directory_to_subdirs = { } <EOL> self . _inotify_events = '<STR_LIT>' <EOL> self . _inotify_fd = _libc . inotify_init ( ) <EOL> if self . _inotify_fd < <NUM_LIT:0> : <EOL> error = OSError ( '<STR_LIT>' ) <EOL> error . errno = ctypes . get_errno ( ) <EOL> error . strerror = errno . errorcode [ ctypes . get_errno ( ) ] <EOL> raise error <EOL> self . _inotify_poll = select . poll ( ) <EOL> self . _inotify_fd_lock = threading . Lock ( ) <EOL> def _remove_watch_for_path ( self , path ) : <EOL> logging . debug ( '<STR_LIT>' , path ) <EOL> wd = self . _directory_to_watch_descriptor [ path ] <EOL> if _libc . inotify_rm_watch ( self . _inotify_fd , wd ) < <NUM_LIT:0> : <EOL> logging . debug ( '<STR_LIT>' , <EOL> path , <EOL> ctypes . get_errno ( ) , <EOL> errno . errorcode [ ctypes . get_errno ( ) ] ) <EOL> parent_path = os . path . dirname ( path ) <EOL> if parent_path in self . _directory_to_subdirs : <EOL> self . _directory_to_subdirs [ parent_path ] . remove ( path ) <EOL> for subdir in frozenset ( self . _directory_to_subdirs [ path ] ) : <EOL> self . _remove_watch_for_path ( subdir ) <EOL> del self . _watch_to_directory [ wd ] <EOL> del self . _directory_to_watch_descriptor [ path ] <EOL> del self . _directory_to_subdirs [ path ] <EOL> def _add_watch_for_path ( self , path ) : <EOL> logging . debug ( '<STR_LIT>' , path ) <EOL> for dirpath , directories , _ in itertools . chain ( <EOL> [ ( os . path . dirname ( path ) , [ os . path . basename ( path ) ] , None ) ] , <EOL> os . walk ( path , topdown = True , followlinks = True ) ) : <EOL> watcher_common . skip_ignored_dirs ( directories ) <EOL> watcher_common . skip_local_symlinks ( <EOL> self . _real_directories , dirpath , directories ) <EOL> for directory in directories : <EOL> directory_path = os . path . join ( dirpath , directory ) <EOL> parent_path = os . path . dirname ( directory_path ) <EOL> watch_descriptor = _libc . inotify_add_watch ( <EOL> self . _inotify_fd , <EOL> ctypes . create_string_buffer ( directory_path ) , <EOL> _INTERESTING_INOTIFY_EVENTS ) <EOL> if watch_descriptor < <NUM_LIT:0> : <EOL> if ctypes . get_errno ( ) == errno . ENOSPC : <EOL> logging . warning ( <EOL> '<STR_LIT>' <EOL> '<STR_LIT>' <EOL> '<STR_LIT>' <EOL> '<STR_LIT>' ) <EOL> return <EOL> error = OSError ( '<STR_LIT>' % directory_path ) <EOL> error . errno = ctypes . get_errno ( ) <EOL> error . strerror = errno . errorcode [ ctypes . get_errno ( ) ] <EOL> error . filename = directory_path <EOL> raise error <EOL> if parent_path in self . _directory_to_subdirs : <EOL> self . _directory_to_subdirs [ parent_path ] . add ( directory_path ) <EOL> self . _watch_to_directory [ watch_descriptor ] = directory_path <EOL> self . _directory_to_watch_descriptor [ directory_path ] = watch_descriptor <EOL> self . _directory_to_subdirs [ directory_path ] = set ( ) <EOL> def start ( self ) : <EOL> """<STR_LIT>""" <EOL> with self . _inotify_fd_lock : <EOL> if self . _inotify_fd < <NUM_LIT:0> : <EOL> return <EOL> self . _inotify_poll . register ( self . _inotify_fd , select . POLLIN ) <EOL> for directory in self . _directories : <EOL> self . _add_watch_for_path ( directory ) <EOL> def quit ( self ) : <EOL> """<STR_LIT>""" <EOL> with self . _inotify_fd_lock : <EOL> os . close ( self . _inotify_fd ) <EOL> self . _inotify_fd = - <NUM_LIT:1> <EOL> def changes ( self , timeout_ms = <NUM_LIT:0> ) : <EOL> """<STR_LIT>""" <EOL> paths = set ( ) <EOL> while True : <EOL> with self . _inotify_fd_lock : <EOL> if self . _inotify_fd < <NUM_LIT:0> : <EOL> return set ( ) <EOL> if not self . _inotify_poll . poll ( <EOL> _AGGREGATE_CHANGES_MS_APART if paths else timeout_ms ) : <EOL> break <EOL> self . _inotify_events += os . read ( self . _inotify_fd , <NUM_LIT> ) <EOL> while len ( self . _inotify_events ) > _INOTIFY_EVENT_SIZE : <EOL> wd , mask , cookie , length = _INOTIFY_EVENT . unpack ( <EOL> self . _inotify_events [ : _INOTIFY_EVENT_SIZE ] ) <EOL> if len ( self . _inotify_events ) < _INOTIFY_EVENT_SIZE + length : <EOL> break <EOL> name = self . _inotify_events [ <EOL> _INOTIFY_EVENT_SIZE : _INOTIFY_EVENT_SIZE + length ] <EOL> name = name . rstrip ( '<STR_LIT>' ) <EOL> logging . debug ( <EOL> '<STR_LIT>' , wd , <EOL> _bit_str ( mask , _ATTRIBUTE_MASK_NAMES ) , cookie , length , name ) <EOL> self . _inotify_events = self . _inotify_events [ <EOL> _INOTIFY_EVENT_SIZE + length : ] <EOL> if mask & IN_IGNORED : <EOL> continue <EOL> try : <EOL> directory = self . _watch_to_directory [ wd ] <EOL> except KeyError : <EOL> logging . debug ( '<STR_LIT>' , wd ) <EOL> continue <EOL> path = os . path . join ( directory , name ) <EOL> if os . path . isdir ( path ) or path in self . _directory_to_watch_descriptor : <EOL> if mask & IN_DELETE : <EOL> self . _remove_watch_for_path ( path ) <EOL> elif mask & IN_MOVED_FROM : <EOL> self . _remove_watch_for_path ( path ) <EOL> elif mask & IN_CREATE : <EOL> self . _add_watch_for_path ( path ) <EOL> elif mask & IN_MOVED_TO : <EOL> self . _add_watch_for_path ( path ) <EOL> if path not in paths and not watcher_common . ignore_file ( path ) : <EOL> paths . add ( path ) <EOL> return paths </s>
