123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631 |
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- #
- # Copyright 2011 Yesudeep Mangalapilly <yesudeep@gmail.com>
- # Copyright 2012 Google, Inc.
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- """
- :module: watchdog.events
- :synopsis: File system events and event handlers.
- :author: yesudeep@google.com (Yesudeep Mangalapilly)
- Event Classes
- -------------
- .. autoclass:: FileSystemEvent
- :members:
- :show-inheritance:
- :inherited-members:
- .. autoclass:: FileSystemMovedEvent
- :members:
- :show-inheritance:
- .. autoclass:: FileMovedEvent
- :members:
- :show-inheritance:
- .. autoclass:: DirMovedEvent
- :members:
- :show-inheritance:
- .. autoclass:: FileModifiedEvent
- :members:
- :show-inheritance:
- .. autoclass:: DirModifiedEvent
- :members:
- :show-inheritance:
- .. autoclass:: FileCreatedEvent
- :members:
- :show-inheritance:
- .. autoclass:: DirCreatedEvent
- :members:
- :show-inheritance:
- .. autoclass:: FileDeletedEvent
- :members:
- :show-inheritance:
- .. autoclass:: DirDeletedEvent
- :members:
- :show-inheritance:
- Event Handler Classes
- ---------------------
- .. autoclass:: FileSystemEventHandler
- :members:
- :show-inheritance:
- .. autoclass:: PatternMatchingEventHandler
- :members:
- :show-inheritance:
- .. autoclass:: RegexMatchingEventHandler
- :members:
- :show-inheritance:
- .. autoclass:: LoggingEventHandler
- :members:
- :show-inheritance:
- """
- import os.path
- import logging
- import re
- from pathtools.patterns import match_any_paths
- from watchdog.utils import has_attribute
- from watchdog.utils import unicode_paths
- EVENT_TYPE_MOVED = 'moved'
- EVENT_TYPE_DELETED = 'deleted'
- EVENT_TYPE_CREATED = 'created'
- EVENT_TYPE_MODIFIED = 'modified'
- class FileSystemEvent(object):
- """
- Immutable type that represents a file system event that is triggered
- when a change occurs on the monitored file system.
- All FileSystemEvent objects are required to be immutable and hence
- can be used as keys in dictionaries or be added to sets.
- """
- event_type = None
- """The type of the event as a string."""
- is_directory = False
- """True if event was emitted for a directory; False otherwise."""
- is_synthetic = False
- """
- True if event was synthesized; False otherwise.
- These are events that weren't actually broadcast by the OS, but
- are presumed to have happened based on other, actual events.
- """
- def __init__(self, src_path):
- self._src_path = src_path
- @property
- def src_path(self):
- """Source path of the file system object that triggered this event."""
- return self._src_path
- def __str__(self):
- return self.__repr__()
- def __repr__(self):
- return ("<%(class_name)s: event_type=%(event_type)s, "
- "src_path=%(src_path)r, "
- "is_directory=%(is_directory)s>"
- ) % (dict(
- class_name=self.__class__.__name__,
- event_type=self.event_type,
- src_path=self.src_path,
- is_directory=self.is_directory))
- # Used for comparison of events.
- @property
- def key(self):
- return (self.event_type, self.src_path, self.is_directory)
- def __eq__(self, event):
- return self.key == event.key
- def __ne__(self, event):
- return self.key != event.key
- def __hash__(self):
- return hash(self.key)
- class FileSystemMovedEvent(FileSystemEvent):
- """
- File system event representing any kind of file system movement.
- """
- event_type = EVENT_TYPE_MOVED
- def __init__(self, src_path, dest_path):
- super(FileSystemMovedEvent, self).__init__(src_path)
- self._dest_path = dest_path
- @property
- def dest_path(self):
- """The destination path of the move event."""
- return self._dest_path
- # Used for hashing this as an immutable object.
- @property
- def key(self):
- return (self.event_type, self.src_path, self.dest_path, self.is_directory)
- def __repr__(self):
- return ("<%(class_name)s: src_path=%(src_path)r, "
- "dest_path=%(dest_path)r, "
- "is_directory=%(is_directory)s>"
- ) % (dict(class_name=self.__class__.__name__,
- src_path=self.src_path,
- dest_path=self.dest_path,
- is_directory=self.is_directory))
- # File events.
- class FileDeletedEvent(FileSystemEvent):
- """File system event representing file deletion on the file system."""
- event_type = EVENT_TYPE_DELETED
- def __init__(self, src_path):
- super(FileDeletedEvent, self).__init__(src_path)
- def __repr__(self):
- return "<%(class_name)s: src_path=%(src_path)r>" %\
- dict(class_name=self.__class__.__name__,
- src_path=self.src_path)
- class FileModifiedEvent(FileSystemEvent):
- """File system event representing file modification on the file system."""
- event_type = EVENT_TYPE_MODIFIED
- def __init__(self, src_path):
- super(FileModifiedEvent, self).__init__(src_path)
- def __repr__(self):
- return ("<%(class_name)s: src_path=%(src_path)r>"
- ) % (dict(class_name=self.__class__.__name__,
- src_path=self.src_path))
- class FileCreatedEvent(FileSystemEvent):
- """File system event representing file creation on the file system."""
- event_type = EVENT_TYPE_CREATED
- def __init__(self, src_path):
- super(FileCreatedEvent, self).__init__(src_path)
- def __repr__(self):
- return ("<%(class_name)s: src_path=%(src_path)r>"
- ) % (dict(class_name=self.__class__.__name__,
- src_path=self.src_path))
- class FileMovedEvent(FileSystemMovedEvent):
- """File system event representing file movement on the file system."""
- def __init__(self, src_path, dest_path):
- super(FileMovedEvent, self).__init__(src_path, dest_path)
- def __repr__(self):
- return ("<%(class_name)s: src_path=%(src_path)r, "
- "dest_path=%(dest_path)r>"
- ) % (dict(class_name=self.__class__.__name__,
- src_path=self.src_path,
- dest_path=self.dest_path))
- # Directory events.
- class DirDeletedEvent(FileSystemEvent):
- """File system event representing directory deletion on the file system."""
- event_type = EVENT_TYPE_DELETED
- is_directory = True
- def __init__(self, src_path):
- super(DirDeletedEvent, self).__init__(src_path)
- def __repr__(self):
- return ("<%(class_name)s: src_path=%(src_path)r>"
- ) % (dict(class_name=self.__class__.__name__,
- src_path=self.src_path))
- class DirModifiedEvent(FileSystemEvent):
- """
- File system event representing directory modification on the file system.
- """
- event_type = EVENT_TYPE_MODIFIED
- is_directory = True
- def __init__(self, src_path):
- super(DirModifiedEvent, self).__init__(src_path)
- def __repr__(self):
- return ("<%(class_name)s: src_path=%(src_path)r>"
- ) % (dict(class_name=self.__class__.__name__,
- src_path=self.src_path))
- class DirCreatedEvent(FileSystemEvent):
- """File system event representing directory creation on the file system."""
- event_type = EVENT_TYPE_CREATED
- is_directory = True
- def __init__(self, src_path):
- super(DirCreatedEvent, self).__init__(src_path)
- def __repr__(self):
- return ("<%(class_name)s: src_path=%(src_path)r>"
- ) % (dict(class_name=self.__class__.__name__,
- src_path=self.src_path))
- class DirMovedEvent(FileSystemMovedEvent):
- """File system event representing directory movement on the file system."""
- is_directory = True
- def __init__(self, src_path, dest_path):
- super(DirMovedEvent, self).__init__(src_path, dest_path)
- def __repr__(self):
- return ("<%(class_name)s: src_path=%(src_path)r, "
- "dest_path=%(dest_path)r>"
- ) % (dict(class_name=self.__class__.__name__,
- src_path=self.src_path,
- dest_path=self.dest_path))
- class FileSystemEventHandler(object):
- """
- Base file system event handler that you can override methods from.
- """
- def dispatch(self, event):
- """Dispatches events to the appropriate methods.
- :param event:
- The event object representing the file system event.
- :type event:
- :class:`FileSystemEvent`
- """
- self.on_any_event(event)
- _method_map = {
- EVENT_TYPE_MODIFIED: self.on_modified,
- EVENT_TYPE_MOVED: self.on_moved,
- EVENT_TYPE_CREATED: self.on_created,
- EVENT_TYPE_DELETED: self.on_deleted,
- }
- event_type = event.event_type
- _method_map[event_type](event)
- def on_any_event(self, event):
- """Catch-all event handler.
- :param event:
- The event object representing the file system event.
- :type event:
- :class:`FileSystemEvent`
- """
- def on_moved(self, event):
- """Called when a file or a directory is moved or renamed.
- :param event:
- Event representing file/directory movement.
- :type event:
- :class:`DirMovedEvent` or :class:`FileMovedEvent`
- """
- def on_created(self, event):
- """Called when a file or directory is created.
- :param event:
- Event representing file/directory creation.
- :type event:
- :class:`DirCreatedEvent` or :class:`FileCreatedEvent`
- """
- def on_deleted(self, event):
- """Called when a file or directory is deleted.
- :param event:
- Event representing file/directory deletion.
- :type event:
- :class:`DirDeletedEvent` or :class:`FileDeletedEvent`
- """
- def on_modified(self, event):
- """Called when a file or directory is modified.
- :param event:
- Event representing file/directory modification.
- :type event:
- :class:`DirModifiedEvent` or :class:`FileModifiedEvent`
- """
- class PatternMatchingEventHandler(FileSystemEventHandler):
- """
- Matches given patterns with file paths associated with occurring events.
- """
- def __init__(self, patterns=None, ignore_patterns=None,
- ignore_directories=False, case_sensitive=False):
- super(PatternMatchingEventHandler, self).__init__()
- self._patterns = patterns
- self._ignore_patterns = ignore_patterns
- self._ignore_directories = ignore_directories
- self._case_sensitive = case_sensitive
- @property
- def patterns(self):
- """
- (Read-only)
- Patterns to allow matching event paths.
- """
- return self._patterns
- @property
- def ignore_patterns(self):
- """
- (Read-only)
- Patterns to ignore matching event paths.
- """
- return self._ignore_patterns
- @property
- def ignore_directories(self):
- """
- (Read-only)
- ``True`` if directories should be ignored; ``False`` otherwise.
- """
- return self._ignore_directories
- @property
- def case_sensitive(self):
- """
- (Read-only)
- ``True`` if path names should be matched sensitive to case; ``False``
- otherwise.
- """
- return self._case_sensitive
- def dispatch(self, event):
- """Dispatches events to the appropriate methods.
- :param event:
- The event object representing the file system event.
- :type event:
- :class:`FileSystemEvent`
- """
- if self.ignore_directories and event.is_directory:
- return
- paths = []
- if has_attribute(event, 'dest_path'):
- paths.append(unicode_paths.decode(event.dest_path))
- if event.src_path:
- paths.append(unicode_paths.decode(event.src_path))
- if match_any_paths(paths,
- included_patterns=self.patterns,
- excluded_patterns=self.ignore_patterns,
- case_sensitive=self.case_sensitive):
- self.on_any_event(event)
- _method_map = {
- EVENT_TYPE_MODIFIED: self.on_modified,
- EVENT_TYPE_MOVED: self.on_moved,
- EVENT_TYPE_CREATED: self.on_created,
- EVENT_TYPE_DELETED: self.on_deleted,
- }
- event_type = event.event_type
- _method_map[event_type](event)
- class RegexMatchingEventHandler(FileSystemEventHandler):
- """
- Matches given regexes with file paths associated with occurring events.
- """
- def __init__(self, regexes=[r".*"], ignore_regexes=[],
- ignore_directories=False, case_sensitive=False):
- super(RegexMatchingEventHandler, self).__init__()
- if case_sensitive:
- self._regexes = [re.compile(r) for r in regexes]
- self._ignore_regexes = [re.compile(r) for r in ignore_regexes]
- else:
- self._regexes = [re.compile(r, re.I) for r in regexes]
- self._ignore_regexes = [re.compile(r, re.I) for r in ignore_regexes]
- self._ignore_directories = ignore_directories
- self._case_sensitive = case_sensitive
- @property
- def regexes(self):
- """
- (Read-only)
- Regexes to allow matching event paths.
- """
- return self._regexes
- @property
- def ignore_regexes(self):
- """
- (Read-only)
- Regexes to ignore matching event paths.
- """
- return self._ignore_regexes
- @property
- def ignore_directories(self):
- """
- (Read-only)
- ``True`` if directories should be ignored; ``False`` otherwise.
- """
- return self._ignore_directories
- @property
- def case_sensitive(self):
- """
- (Read-only)
- ``True`` if path names should be matched sensitive to case; ``False``
- otherwise.
- """
- return self._case_sensitive
- def dispatch(self, event):
- """Dispatches events to the appropriate methods.
- :param event:
- The event object representing the file system event.
- :type event:
- :class:`FileSystemEvent`
- """
- if self.ignore_directories and event.is_directory:
- return
- paths = []
- if has_attribute(event, 'dest_path'):
- paths.append(unicode_paths.decode(event.dest_path))
- if event.src_path:
- paths.append(unicode_paths.decode(event.src_path))
- if any(r.match(p) for r in self.ignore_regexes for p in paths):
- return
- if any(r.match(p) for r in self.regexes for p in paths):
- self.on_any_event(event)
- _method_map = {
- EVENT_TYPE_MODIFIED: self.on_modified,
- EVENT_TYPE_MOVED: self.on_moved,
- EVENT_TYPE_CREATED: self.on_created,
- EVENT_TYPE_DELETED: self.on_deleted,
- }
- event_type = event.event_type
- _method_map[event_type](event)
- class LoggingEventHandler(FileSystemEventHandler):
- """Logs all the events captured."""
- def on_moved(self, event):
- super(LoggingEventHandler, self).on_moved(event)
- what = 'directory' if event.is_directory else 'file'
- logging.info("Moved %s: from %s to %s", what, event.src_path,
- event.dest_path)
- def on_created(self, event):
- super(LoggingEventHandler, self).on_created(event)
- what = 'directory' if event.is_directory else 'file'
- logging.info("Created %s: %s", what, event.src_path)
- def on_deleted(self, event):
- super(LoggingEventHandler, self).on_deleted(event)
- what = 'directory' if event.is_directory else 'file'
- logging.info("Deleted %s: %s", what, event.src_path)
- def on_modified(self, event):
- super(LoggingEventHandler, self).on_modified(event)
- what = 'directory' if event.is_directory else 'file'
- logging.info("Modified %s: %s", what, event.src_path)
- class LoggingFileSystemEventHandler(LoggingEventHandler):
- """
- For backwards-compatibility. Please use :class:`LoggingEventHandler`
- instead.
- """
- def generate_sub_moved_events(src_dir_path, dest_dir_path):
- """Generates an event list of :class:`DirMovedEvent` and
- :class:`FileMovedEvent` objects for all the files and directories within
- the given moved directory that were moved along with the directory.
- :param src_dir_path:
- The source path of the moved directory.
- :param dest_dir_path:
- The destination path of the moved directory.
- :returns:
- An iterable of file system events of type :class:`DirMovedEvent` and
- :class:`FileMovedEvent`.
- """
- for root, directories, filenames in os.walk(dest_dir_path):
- for directory in directories:
- full_path = os.path.join(root, directory)
- renamed_path = full_path.replace(dest_dir_path, src_dir_path) if src_dir_path else None
- event = DirMovedEvent(renamed_path, full_path)
- event.is_synthetic = True
- yield event
- for filename in filenames:
- full_path = os.path.join(root, filename)
- renamed_path = full_path.replace(dest_dir_path, src_dir_path) if src_dir_path else None
- event = FileMovedEvent(renamed_path, full_path)
- event.is_synthetic = True
- yield event
- def generate_sub_created_events(src_dir_path):
- """Generates an event list of :class:`DirCreatedEvent` and
- :class:`FileCreatedEvent` objects for all the files and directories within
- the given moved directory that were moved along with the directory.
- :param src_dir_path:
- The source path of the created directory.
- :returns:
- An iterable of file system events of type :class:`DirCreatedEvent` and
- :class:`FileCreatedEvent`.
- """
- for root, directories, filenames in os.walk(src_dir_path):
- for directory in directories:
- event = DirCreatedEvent(os.path.join(root, directory))
- event.is_synthetic = True
- yield event
- for filename in filenames:
- event = FileCreatedEvent(os.path.join(root, filename))
- event.is_synthetic = True
- yield event
|