Added minimal version of watchdog
This commit is contained in:
parent
20ccc1460e
commit
5611ff2f93
26 changed files with 5195 additions and 0 deletions
158
Sublime/fl0w/watchdog/utils/__init__.py
Normal file
158
Sublime/fl0w/watchdog/utils/__init__.py
Normal file
|
@ -0,0 +1,158 @@
|
|||
#!/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.utils
|
||||
:synopsis: Utility classes and functions.
|
||||
:author: yesudeep@google.com (Yesudeep Mangalapilly)
|
||||
|
||||
Classes
|
||||
-------
|
||||
.. autoclass:: BaseThread
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:inherited-members:
|
||||
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
import watchdog.utils.platform
|
||||
from watchdog.utils.compat import Event
|
||||
from collections import namedtuple
|
||||
|
||||
|
||||
if sys.version_info[0] == 2 and platform.is_windows():
|
||||
# st_ino is not implemented in os.stat on this platform
|
||||
import win32stat
|
||||
stat = win32stat.stat
|
||||
else:
|
||||
stat = os.stat
|
||||
|
||||
|
||||
def has_attribute(ob, attribute):
|
||||
"""
|
||||
:func:`hasattr` swallows exceptions. :func:`has_attribute` tests a Python object for the
|
||||
presence of an attribute.
|
||||
|
||||
:param ob:
|
||||
object to inspect
|
||||
:param attribute:
|
||||
``str`` for the name of the attribute.
|
||||
"""
|
||||
return getattr(ob, attribute, None) is not None
|
||||
|
||||
|
||||
class UnsupportedLibc(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class BaseThread(threading.Thread):
|
||||
""" Convenience class for creating stoppable threads. """
|
||||
|
||||
def __init__(self):
|
||||
threading.Thread.__init__(self)
|
||||
if has_attribute(self, 'daemon'):
|
||||
self.daemon = True
|
||||
else:
|
||||
self.setDaemon(True)
|
||||
self._stopped_event = Event()
|
||||
|
||||
if not has_attribute(self._stopped_event, 'is_set'):
|
||||
self._stopped_event.is_set = self._stopped_event.isSet
|
||||
|
||||
@property
|
||||
def stopped_event(self):
|
||||
return self._stopped_event
|
||||
|
||||
def should_keep_running(self):
|
||||
"""Determines whether the thread should continue running."""
|
||||
return not self._stopped_event.is_set()
|
||||
|
||||
def on_thread_stop(self):
|
||||
"""Override this method instead of :meth:`stop()`.
|
||||
:meth:`stop()` calls this method.
|
||||
|
||||
This method is called immediately after the thread is signaled to stop.
|
||||
"""
|
||||
pass
|
||||
|
||||
def stop(self):
|
||||
"""Signals the thread to stop."""
|
||||
self._stopped_event.set()
|
||||
self.on_thread_stop()
|
||||
|
||||
def on_thread_start(self):
|
||||
"""Override this method instead of :meth:`start()`. :meth:`start()`
|
||||
calls this method.
|
||||
|
||||
This method is called right before this thread is started and this
|
||||
object’s run() method is invoked.
|
||||
"""
|
||||
pass
|
||||
|
||||
def start(self):
|
||||
self.on_thread_start()
|
||||
threading.Thread.start(self)
|
||||
|
||||
|
||||
def load_module(module_name):
|
||||
"""Imports a module given its name and returns a handle to it."""
|
||||
try:
|
||||
__import__(module_name)
|
||||
except ImportError:
|
||||
raise ImportError('No module named %s' % module_name)
|
||||
return sys.modules[module_name]
|
||||
|
||||
|
||||
def load_class(dotted_path):
|
||||
"""Loads and returns a class definition provided a dotted path
|
||||
specification the last part of the dotted path is the class name
|
||||
and there is at least one module name preceding the class name.
|
||||
|
||||
Notes:
|
||||
You will need to ensure that the module you are trying to load
|
||||
exists in the Python path.
|
||||
|
||||
Examples:
|
||||
- module.name.ClassName # Provided module.name is in the Python path.
|
||||
- module.ClassName # Provided module is in the Python path.
|
||||
|
||||
What won't work:
|
||||
- ClassName
|
||||
- modle.name.ClassName # Typo in module name.
|
||||
- module.name.ClasNam # Typo in classname.
|
||||
"""
|
||||
dotted_path_split = dotted_path.split('.')
|
||||
if len(dotted_path_split) > 1:
|
||||
klass_name = dotted_path_split[-1]
|
||||
module_name = '.'.join(dotted_path_split[:-1])
|
||||
|
||||
module = load_module(module_name)
|
||||
if has_attribute(module, klass_name):
|
||||
klass = getattr(module, klass_name)
|
||||
return klass
|
||||
# Finally create and return an instance of the class
|
||||
# return klass(*args, **kwargs)
|
||||
else:
|
||||
raise AttributeError('Module %s does not have class attribute %s' % (
|
||||
module_name, klass_name))
|
||||
else:
|
||||
raise ValueError(
|
||||
'Dotted module path %s must contain a module name and a classname' % dotted_path)
|
249
Sublime/fl0w/watchdog/utils/bricks.py
Normal file
249
Sublime/fl0w/watchdog/utils/bricks.py
Normal file
|
@ -0,0 +1,249 @@
|
|||
#!/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.
|
||||
|
||||
|
||||
"""
|
||||
Utility collections or "bricks".
|
||||
|
||||
:module: watchdog.utils.bricks
|
||||
:author: yesudeep@google.com (Yesudeep Mangalapilly)
|
||||
:author: lalinsky@gmail.com (Lukáš Lalinský)
|
||||
:author: python@rcn.com (Raymond Hettinger)
|
||||
|
||||
Classes
|
||||
=======
|
||||
.. autoclass:: OrderedSetQueue
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: OrderedSet
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import collections
|
||||
from .compat import queue
|
||||
|
||||
class SkipRepeatsQueue(queue.Queue):
|
||||
|
||||
"""Thread-safe implementation of an special queue where a
|
||||
put of the last-item put'd will be dropped.
|
||||
|
||||
The implementation leverages locking already implemented in the base class
|
||||
redefining only the primitives.
|
||||
|
||||
Queued items must be immutable and hashable so that they can be used
|
||||
as dictionary keys. You must implement **only read-only properties** and
|
||||
the :meth:`Item.__hash__()`, :meth:`Item.__eq__()`, and
|
||||
:meth:`Item.__ne__()` methods for items to be hashable.
|
||||
|
||||
An example implementation follows::
|
||||
|
||||
class Item(object):
|
||||
def __init__(self, a, b):
|
||||
self._a = a
|
||||
self._b = b
|
||||
|
||||
@property
|
||||
def a(self):
|
||||
return self._a
|
||||
|
||||
@property
|
||||
def b(self):
|
||||
return self._b
|
||||
|
||||
def _key(self):
|
||||
return (self._a, self._b)
|
||||
|
||||
def __eq__(self, item):
|
||||
return self._key() == item._key()
|
||||
|
||||
def __ne__(self, item):
|
||||
return self._key() != item._key()
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self._key())
|
||||
|
||||
based on the OrderedSetQueue below
|
||||
"""
|
||||
|
||||
def _init(self, maxsize):
|
||||
queue.Queue._init(self, maxsize)
|
||||
self._last_item = None
|
||||
|
||||
def _put(self, item):
|
||||
if item != self._last_item:
|
||||
queue.Queue._put(self, item)
|
||||
self._last_item = item
|
||||
else:
|
||||
# `put` increments `unfinished_tasks` even if we did not put
|
||||
# anything into the queue here
|
||||
self.unfinished_tasks -= 1
|
||||
|
||||
def _get(self):
|
||||
item = queue.Queue._get(self)
|
||||
if item is self._last_item:
|
||||
self._last_item = None
|
||||
return item
|
||||
|
||||
|
||||
class OrderedSetQueue(queue.Queue):
|
||||
|
||||
"""Thread-safe implementation of an ordered set queue.
|
||||
|
||||
Disallows adding a duplicate item while maintaining the
|
||||
order of items in the queue. The implementation leverages
|
||||
locking already implemented in the base class
|
||||
redefining only the primitives. Since the internal queue
|
||||
is not replaced, the order is maintained. The set is used
|
||||
merely to check for the existence of an item.
|
||||
|
||||
Queued items must be immutable and hashable so that they can be used
|
||||
as dictionary keys. You must implement **only read-only properties** and
|
||||
the :meth:`Item.__hash__()`, :meth:`Item.__eq__()`, and
|
||||
:meth:`Item.__ne__()` methods for items to be hashable.
|
||||
|
||||
An example implementation follows::
|
||||
|
||||
class Item(object):
|
||||
def __init__(self, a, b):
|
||||
self._a = a
|
||||
self._b = b
|
||||
|
||||
@property
|
||||
def a(self):
|
||||
return self._a
|
||||
|
||||
@property
|
||||
def b(self):
|
||||
return self._b
|
||||
|
||||
def _key(self):
|
||||
return (self._a, self._b)
|
||||
|
||||
def __eq__(self, item):
|
||||
return self._key() == item._key()
|
||||
|
||||
def __ne__(self, item):
|
||||
return self._key() != item._key()
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self._key())
|
||||
|
||||
:author: lalinsky@gmail.com (Lukáš Lalinský)
|
||||
:url: http://stackoverflow.com/questions/1581895/how-check-if-a-task-is-already-in-python-queue
|
||||
"""
|
||||
|
||||
def _init(self, maxsize):
|
||||
queue.Queue._init(self, maxsize)
|
||||
self._set_of_items = set()
|
||||
|
||||
def _put(self, item):
|
||||
if item not in self._set_of_items:
|
||||
queue.Queue._put(self, item)
|
||||
self._set_of_items.add(item)
|
||||
else:
|
||||
# `put` increments `unfinished_tasks` even if we did not put
|
||||
# anything into the queue here
|
||||
self.unfinished_tasks -= 1
|
||||
|
||||
def _get(self):
|
||||
item = queue.Queue._get(self)
|
||||
self._set_of_items.remove(item)
|
||||
return item
|
||||
|
||||
|
||||
if sys.version_info >= (2, 6, 0):
|
||||
KEY, PREV, NEXT = list(range(3))
|
||||
|
||||
class OrderedSet(collections.MutableSet):
|
||||
|
||||
"""
|
||||
Implementation based on a doubly-linked link and an internal dictionary.
|
||||
This design gives :class:`OrderedSet` the same big-Oh running times as
|
||||
regular sets including O(1) adds, removes, and lookups as well as
|
||||
O(n) iteration.
|
||||
|
||||
.. ADMONITION:: Implementation notes
|
||||
|
||||
Runs on Python 2.6 or later (and runs on Python 3.0 or later
|
||||
without any modifications).
|
||||
|
||||
:author: python@rcn.com (Raymond Hettinger)
|
||||
:url: http://code.activestate.com/recipes/576694/
|
||||
"""
|
||||
|
||||
def __init__(self, iterable=None):
|
||||
self.end = end = []
|
||||
end += [None, end, end] # sentinel node for doubly linked list
|
||||
self.map = {} # key --> [key, prev, next]
|
||||
if iterable is not None:
|
||||
self |= iterable
|
||||
|
||||
def __len__(self):
|
||||
return len(self.map)
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self.map
|
||||
|
||||
def add(self, key):
|
||||
if key not in self.map:
|
||||
end = self.end
|
||||
curr = end[PREV]
|
||||
curr[NEXT] = end[PREV] = self.map[key] = [key, curr, end]
|
||||
|
||||
def discard(self, key):
|
||||
if key in self.map:
|
||||
key, prev, _next = self.map.pop(key)
|
||||
prev[NEXT] = _next
|
||||
_next[PREV] = prev
|
||||
|
||||
def __iter__(self):
|
||||
end = self.end
|
||||
curr = end[NEXT]
|
||||
while curr is not end:
|
||||
yield curr[KEY]
|
||||
curr = curr[NEXT]
|
||||
|
||||
def __reversed__(self):
|
||||
end = self.end
|
||||
curr = end[PREV]
|
||||
while curr is not end:
|
||||
yield curr[KEY]
|
||||
curr = curr[PREV]
|
||||
|
||||
def pop(self, last=True):
|
||||
if not self:
|
||||
raise KeyError('set is empty')
|
||||
key = next(reversed(self)) if last else next(iter(self))
|
||||
self.discard(key)
|
||||
return key
|
||||
|
||||
def __repr__(self):
|
||||
if not self:
|
||||
return '%s()' % (self.__class__.__name__,)
|
||||
return '%s(%r)' % (self.__class__.__name__, list(self))
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, OrderedSet):
|
||||
return len(self) == len(other) and list(self) == list(other)
|
||||
return set(self) == set(other)
|
||||
|
||||
def __del__(self):
|
||||
self.clear() # remove circular references
|
29
Sublime/fl0w/watchdog/utils/compat.py
Normal file
29
Sublime/fl0w/watchdog/utils/compat.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2014 Thomas Amland <thomas.amland@gmail.com>
|
||||
#
|
||||
# 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.
|
||||
import sys
|
||||
|
||||
__all__ = ['queue', 'Event']
|
||||
|
||||
try:
|
||||
import queue
|
||||
except ImportError:
|
||||
import Queue as queue
|
||||
|
||||
|
||||
if sys.version_info < (2, 7):
|
||||
from watchdog.utils.event_backport import Event
|
||||
else:
|
||||
from threading import Event
|
198
Sublime/fl0w/watchdog/utils/decorators.py
Normal file
198
Sublime/fl0w/watchdog/utils/decorators.py
Normal file
|
@ -0,0 +1,198 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Most of this code was obtained from the Python documentation online.
|
||||
|
||||
"""Decorator utility functions.
|
||||
|
||||
decorators:
|
||||
- synchronized
|
||||
- propertyx
|
||||
- accepts
|
||||
- returns
|
||||
- singleton
|
||||
- attrs
|
||||
- deprecated
|
||||
"""
|
||||
|
||||
import functools
|
||||
import warnings
|
||||
import threading
|
||||
import sys
|
||||
|
||||
|
||||
def synchronized(lock=None):
|
||||
"""Decorator that synchronizes a method or a function with a mutex lock.
|
||||
|
||||
Example usage:
|
||||
|
||||
@synchronized()
|
||||
def operation(self, a, b):
|
||||
...
|
||||
"""
|
||||
if lock is None:
|
||||
lock = threading.Lock()
|
||||
|
||||
def wrapper(function):
|
||||
def new_function(*args, **kwargs):
|
||||
lock.acquire()
|
||||
try:
|
||||
return function(*args, **kwargs)
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
return new_function
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def propertyx(function):
|
||||
"""Decorator to easily create properties in classes.
|
||||
|
||||
Example:
|
||||
|
||||
class Angle(object):
|
||||
def __init__(self, rad):
|
||||
self._rad = rad
|
||||
|
||||
@property
|
||||
def rad():
|
||||
def fget(self):
|
||||
return self._rad
|
||||
def fset(self, angle):
|
||||
if isinstance(angle, Angle):
|
||||
angle = angle.rad
|
||||
self._rad = float(angle)
|
||||
|
||||
Arguments:
|
||||
- `function`: The function to be decorated.
|
||||
"""
|
||||
keys = ('fget', 'fset', 'fdel')
|
||||
func_locals = {'doc': function.__doc__}
|
||||
|
||||
def probe_func(frame, event, arg):
|
||||
if event == 'return':
|
||||
locals = frame.f_locals
|
||||
func_locals.update(dict((k, locals.get(k)) for k in keys))
|
||||
sys.settrace(None)
|
||||
return probe_func
|
||||
|
||||
sys.settrace(probe_func)
|
||||
function()
|
||||
return property(**func_locals)
|
||||
|
||||
|
||||
def accepts(*types):
|
||||
"""Decorator to ensure that the decorated function accepts the given types as arguments.
|
||||
|
||||
Example:
|
||||
@accepts(int, (int,float))
|
||||
@returns((int,float))
|
||||
def func(arg1, arg2):
|
||||
return arg1 * arg2
|
||||
"""
|
||||
|
||||
def check_accepts(f):
|
||||
assert len(types) == f.__code__.co_argcount
|
||||
|
||||
def new_f(*args, **kwds):
|
||||
for (a, t) in zip(args, types):
|
||||
assert isinstance(a, t),\
|
||||
"arg %r does not match %s" % (a, t)
|
||||
return f(*args, **kwds)
|
||||
|
||||
new_f.__name__ = f.__name__
|
||||
return new_f
|
||||
|
||||
return check_accepts
|
||||
|
||||
|
||||
def returns(rtype):
|
||||
"""Decorator to ensure that the decorated function returns the given
|
||||
type as argument.
|
||||
|
||||
Example:
|
||||
@accepts(int, (int,float))
|
||||
@returns((int,float))
|
||||
def func(arg1, arg2):
|
||||
return arg1 * arg2
|
||||
"""
|
||||
|
||||
def check_returns(f):
|
||||
def new_f(*args, **kwds):
|
||||
result = f(*args, **kwds)
|
||||
assert isinstance(result, rtype),\
|
||||
"return value %r does not match %s" % (result, rtype)
|
||||
return result
|
||||
|
||||
new_f.__name__ = f.__name__
|
||||
return new_f
|
||||
|
||||
return check_returns
|
||||
|
||||
|
||||
def singleton(cls):
|
||||
"""Decorator to ensures a class follows the singleton pattern.
|
||||
|
||||
Example:
|
||||
@singleton
|
||||
class MyClass:
|
||||
...
|
||||
"""
|
||||
instances = {}
|
||||
|
||||
def getinstance():
|
||||
if cls not in instances:
|
||||
instances[cls] = cls()
|
||||
return instances[cls]
|
||||
|
||||
return getinstance
|
||||
|
||||
|
||||
def attrs(**kwds):
|
||||
"""Decorator to add attributes to a function.
|
||||
|
||||
Example:
|
||||
|
||||
@attrs(versionadded="2.2",
|
||||
author="Guido van Rossum")
|
||||
def mymethod(f):
|
||||
...
|
||||
"""
|
||||
|
||||
def decorate(f):
|
||||
for k in kwds:
|
||||
setattr(f, k, kwds[k])
|
||||
return f
|
||||
|
||||
return decorate
|
||||
|
||||
|
||||
def deprecated(func):
|
||||
"""This is a decorator which can be used to mark functions
|
||||
as deprecated. It will result in a warning being emitted
|
||||
when the function is used.
|
||||
|
||||
## Usage examples ##
|
||||
@deprecated
|
||||
def my_func():
|
||||
pass
|
||||
|
||||
@other_decorators_must_be_upper
|
||||
@deprecated
|
||||
def my_func():
|
||||
pass
|
||||
"""
|
||||
|
||||
@functools.wraps(func)
|
||||
def new_func(*args, **kwargs):
|
||||
warnings.warn_explicit(
|
||||
"Call to deprecated function %(funcname)s." % {
|
||||
'funcname': func.__name__,
|
||||
},
|
||||
category=DeprecationWarning,
|
||||
filename=func.__code__.co_filename,
|
||||
lineno=func.__code__.co_firstlineno + 1
|
||||
)
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return new_func
|
88
Sublime/fl0w/watchdog/utils/delayed_queue.py
Normal file
88
Sublime/fl0w/watchdog/utils/delayed_queue.py
Normal file
|
@ -0,0 +1,88 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2014 Thomas Amland <thomas.amland@gmail.com>
|
||||
#
|
||||
# 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.
|
||||
|
||||
import time
|
||||
import threading
|
||||
from collections import deque
|
||||
|
||||
|
||||
class DelayedQueue(object):
|
||||
|
||||
def __init__(self, delay):
|
||||
self.delay = delay
|
||||
self._lock = threading.Lock()
|
||||
self._not_empty = threading.Condition(self._lock)
|
||||
self._queue = deque()
|
||||
self._closed = False
|
||||
|
||||
def put(self, element):
|
||||
"""Add element to queue."""
|
||||
self._lock.acquire()
|
||||
self._queue.append((element, time.time()))
|
||||
self._not_empty.notify()
|
||||
self._lock.release()
|
||||
|
||||
def close(self):
|
||||
"""Close queue, indicating no more items will be added."""
|
||||
self._closed = True
|
||||
# Interrupt the blocking _not_empty.wait() call in get
|
||||
self._not_empty.acquire()
|
||||
self._not_empty.notify()
|
||||
self._not_empty.release()
|
||||
|
||||
def get(self):
|
||||
"""Remove and return an element from the queue, or this queue has been
|
||||
closed raise the Closed exception.
|
||||
"""
|
||||
while True:
|
||||
# wait for element to be added to queue
|
||||
self._not_empty.acquire()
|
||||
while len(self._queue) == 0 and not self._closed:
|
||||
self._not_empty.wait()
|
||||
|
||||
if self._closed:
|
||||
self._not_empty.release()
|
||||
return None
|
||||
head, insert_time = self._queue[0]
|
||||
self._not_empty.release()
|
||||
|
||||
# wait for delay
|
||||
time_left = insert_time + self.delay - time.time()
|
||||
while time_left > 0:
|
||||
time.sleep(time_left)
|
||||
time_left = insert_time + self.delay - time.time()
|
||||
|
||||
# return element if it's still in the queue
|
||||
self._lock.acquire()
|
||||
try:
|
||||
if len(self._queue) > 0 and self._queue[0][0] is head:
|
||||
self._queue.popleft()
|
||||
return head
|
||||
finally:
|
||||
self._lock.release()
|
||||
|
||||
def remove(self, predicate):
|
||||
"""Remove and return the first items for which predicate is True,
|
||||
ignoring delay."""
|
||||
try:
|
||||
self._lock.acquire()
|
||||
for i, (elem, t) in enumerate(self._queue):
|
||||
if predicate(elem):
|
||||
del self._queue[i]
|
||||
return elem
|
||||
finally:
|
||||
self._lock.release()
|
||||
return None
|
294
Sublime/fl0w/watchdog/utils/dirsnapshot.py
Normal file
294
Sublime/fl0w/watchdog/utils/dirsnapshot.py
Normal file
|
@ -0,0 +1,294 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2011 Yesudeep Mangalapilly <yesudeep@gmail.com>
|
||||
# Copyright 2012 Google, Inc.
|
||||
# Copyright 2014 Thomas Amland <thomas.amland@gmail.com>
|
||||
#
|
||||
# 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.utils.dirsnapshot
|
||||
:synopsis: Directory snapshots and comparison.
|
||||
:author: yesudeep@google.com (Yesudeep Mangalapilly)
|
||||
|
||||
.. ADMONITION:: Where are the moved events? They "disappeared"
|
||||
|
||||
This implementation does not take partition boundaries
|
||||
into consideration. It will only work when the directory
|
||||
tree is entirely on the same file system. More specifically,
|
||||
any part of the code that depends on inode numbers can
|
||||
break if partition boundaries are crossed. In these cases,
|
||||
the snapshot diff will represent file/directory movement as
|
||||
created and deleted events.
|
||||
|
||||
Classes
|
||||
-------
|
||||
.. autoclass:: DirectorySnapshot
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: DirectorySnapshotDiff
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
"""
|
||||
|
||||
import errno
|
||||
import os
|
||||
from stat import S_ISDIR
|
||||
from watchdog.utils import platform
|
||||
from watchdog.utils import stat as default_stat
|
||||
|
||||
|
||||
class DirectorySnapshotDiff(object):
|
||||
"""
|
||||
Compares two directory snapshots and creates an object that represents
|
||||
the difference between the two snapshots.
|
||||
|
||||
:param ref:
|
||||
The reference directory snapshot.
|
||||
:type ref:
|
||||
:class:`DirectorySnapshot`
|
||||
:param snapshot:
|
||||
The directory snapshot which will be compared
|
||||
with the reference snapshot.
|
||||
:type snapshot:
|
||||
:class:`DirectorySnapshot`
|
||||
"""
|
||||
|
||||
def __init__(self, ref, snapshot):
|
||||
created = snapshot.paths - ref.paths
|
||||
deleted = ref.paths - snapshot.paths
|
||||
|
||||
# check that all unchanged paths have the same inode
|
||||
for path in ref.paths & snapshot.paths:
|
||||
if ref.inode(path) != snapshot.inode(path):
|
||||
created.add(path)
|
||||
deleted.add(path)
|
||||
|
||||
# find moved paths
|
||||
moved = set()
|
||||
for path in set(deleted):
|
||||
inode = ref.inode(path)
|
||||
new_path = snapshot.path(inode)
|
||||
if new_path:
|
||||
# file is not deleted but moved
|
||||
deleted.remove(path)
|
||||
moved.add((path, new_path))
|
||||
|
||||
for path in set(created):
|
||||
inode = snapshot.inode(path)
|
||||
old_path = ref.path(inode)
|
||||
if old_path:
|
||||
created.remove(path)
|
||||
moved.add((old_path, path))
|
||||
|
||||
# find modified paths
|
||||
# first check paths that have not moved
|
||||
modified = set()
|
||||
for path in ref.paths & snapshot.paths:
|
||||
if ref.inode(path) == snapshot.inode(path):
|
||||
if ref.mtime(path) != snapshot.mtime(path):
|
||||
modified.add(path)
|
||||
|
||||
for (old_path, new_path) in moved:
|
||||
if ref.mtime(old_path) != snapshot.mtime(new_path):
|
||||
modified.add(old_path)
|
||||
|
||||
self._dirs_created = [path for path in created if snapshot.isdir(path)]
|
||||
self._dirs_deleted = [path for path in deleted if ref.isdir(path)]
|
||||
self._dirs_modified = [path for path in modified if ref.isdir(path)]
|
||||
self._dirs_moved = [(frm, to) for (frm, to) in moved if ref.isdir(frm)]
|
||||
|
||||
self._files_created = list(created - set(self._dirs_created))
|
||||
self._files_deleted = list(deleted - set(self._dirs_deleted))
|
||||
self._files_modified = list(modified - set(self._dirs_modified))
|
||||
self._files_moved = list(moved - set(self._dirs_moved))
|
||||
|
||||
@property
|
||||
def files_created(self):
|
||||
"""List of files that were created."""
|
||||
return self._files_created
|
||||
|
||||
@property
|
||||
def files_deleted(self):
|
||||
"""List of files that were deleted."""
|
||||
return self._files_deleted
|
||||
|
||||
@property
|
||||
def files_modified(self):
|
||||
"""List of files that were modified."""
|
||||
return self._files_modified
|
||||
|
||||
@property
|
||||
def files_moved(self):
|
||||
"""
|
||||
List of files that were moved.
|
||||
|
||||
Each event is a two-tuple the first item of which is the path
|
||||
that has been renamed to the second item in the tuple.
|
||||
"""
|
||||
return self._files_moved
|
||||
|
||||
@property
|
||||
def dirs_modified(self):
|
||||
"""
|
||||
List of directories that were modified.
|
||||
"""
|
||||
return self._dirs_modified
|
||||
|
||||
@property
|
||||
def dirs_moved(self):
|
||||
"""
|
||||
List of directories that were moved.
|
||||
|
||||
Each event is a two-tuple the first item of which is the path
|
||||
that has been renamed to the second item in the tuple.
|
||||
"""
|
||||
return self._dirs_moved
|
||||
|
||||
@property
|
||||
def dirs_deleted(self):
|
||||
"""
|
||||
List of directories that were deleted.
|
||||
"""
|
||||
return self._dirs_deleted
|
||||
|
||||
@property
|
||||
def dirs_created(self):
|
||||
"""
|
||||
List of directories that were created.
|
||||
"""
|
||||
return self._dirs_created
|
||||
|
||||
class DirectorySnapshot(object):
|
||||
"""
|
||||
A snapshot of stat information of files in a directory.
|
||||
|
||||
:param path:
|
||||
The directory path for which a snapshot should be taken.
|
||||
:type path:
|
||||
``str``
|
||||
:param recursive:
|
||||
``True`` if the entire directory tree should be included in the
|
||||
snapshot; ``False`` otherwise.
|
||||
:type recursive:
|
||||
``bool``
|
||||
:param walker_callback:
|
||||
.. deprecated:: 0.7.2
|
||||
:param stat:
|
||||
Use custom stat function that returns a stat structure for path.
|
||||
Currently only st_dev, st_ino, st_mode and st_mtime are needed.
|
||||
|
||||
A function with the signature ``walker_callback(path, stat_info)``
|
||||
which will be called for every entry in the directory tree.
|
||||
:param listdir:
|
||||
Use custom listdir function. See ``os.listdir`` for details.
|
||||
"""
|
||||
|
||||
def __init__(self, path, recursive=True,
|
||||
walker_callback=(lambda p, s: None),
|
||||
stat=default_stat,
|
||||
listdir=os.listdir):
|
||||
self._stat_info = {}
|
||||
self._inode_to_path = {}
|
||||
|
||||
st = stat(path)
|
||||
self._stat_info[path] = st
|
||||
self._inode_to_path[(st.st_ino, st.st_dev)] = path
|
||||
|
||||
def walk(root):
|
||||
try:
|
||||
paths = [os.path.join(root, name) for name in listdir(root)]
|
||||
except OSError as e:
|
||||
# Directory may have been deleted between finding it in the directory
|
||||
# list of its parent and trying to delete its contents. If this
|
||||
# happens we treat it as empty.
|
||||
if e.errno == errno.ENOENT:
|
||||
return
|
||||
else:
|
||||
raise
|
||||
entries = []
|
||||
for p in paths:
|
||||
try:
|
||||
entries.append((p, stat(p)))
|
||||
except OSError:
|
||||
continue
|
||||
for _ in entries:
|
||||
yield _
|
||||
if recursive:
|
||||
for path, st in entries:
|
||||
if S_ISDIR(st.st_mode):
|
||||
for _ in walk(path):
|
||||
yield _
|
||||
|
||||
for p, st in walk(path):
|
||||
i = (st.st_ino, st.st_dev)
|
||||
self._inode_to_path[i] = p
|
||||
self._stat_info[p] = st
|
||||
walker_callback(p, st)
|
||||
|
||||
@property
|
||||
def paths(self):
|
||||
"""
|
||||
Set of file/directory paths in the snapshot.
|
||||
"""
|
||||
return set(self._stat_info.keys())
|
||||
|
||||
def path(self, id):
|
||||
"""
|
||||
Returns path for id. None if id is unknown to this snapshot.
|
||||
"""
|
||||
return self._inode_to_path.get(id)
|
||||
|
||||
def inode(self, path):
|
||||
""" Returns an id for path. """
|
||||
st = self._stat_info[path]
|
||||
return (st.st_ino, st.st_dev)
|
||||
|
||||
def isdir(self, path):
|
||||
return S_ISDIR(self._stat_info[path].st_mode)
|
||||
|
||||
def mtime(self, path):
|
||||
return self._stat_info[path].st_mtime
|
||||
|
||||
def stat_info(self, path):
|
||||
"""
|
||||
Returns a stat information object for the specified path from
|
||||
the snapshot.
|
||||
|
||||
Attached information is subject to change. Do not use unless
|
||||
you specify `stat` in constructor. Use :func:`inode`, :func:`mtime`,
|
||||
:func:`isdir` instead.
|
||||
|
||||
:param path:
|
||||
The path for which stat information should be obtained
|
||||
from a snapshot.
|
||||
"""
|
||||
return self._stat_info[path]
|
||||
|
||||
def __sub__(self, previous_dirsnap):
|
||||
"""Allow subtracting a DirectorySnapshot object instance from
|
||||
another.
|
||||
|
||||
:returns:
|
||||
A :class:`DirectorySnapshotDiff` object.
|
||||
"""
|
||||
return DirectorySnapshotDiff(previous_dirsnap, self)
|
||||
|
||||
def __str__(self):
|
||||
return self.__repr__()
|
||||
|
||||
def __repr__(self):
|
||||
return str(self._stat_info)
|
146
Sublime/fl0w/watchdog/utils/echo.py
Normal file
146
Sublime/fl0w/watchdog/utils/echo.py
Normal file
|
@ -0,0 +1,146 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# echo.py: Tracing function calls using Python decorators.
|
||||
#
|
||||
# Written by Thomas Guest <tag@wordaligned.org>
|
||||
# Please see http://wordaligned.org/articles/echo
|
||||
#
|
||||
# Place into the public domain.
|
||||
|
||||
""" Echo calls made to functions and methods in a module.
|
||||
|
||||
"Echoing" a function call means printing out the name of the function
|
||||
and the values of its arguments before making the call (which is more
|
||||
commonly referred to as "tracing", but Python already has a trace module).
|
||||
|
||||
Example: to echo calls made to functions in "my_module" do:
|
||||
|
||||
import echo
|
||||
import my_module
|
||||
echo.echo_module(my_module)
|
||||
|
||||
Example: to echo calls made to functions in "my_module.my_class" do:
|
||||
|
||||
echo.echo_class(my_module.my_class)
|
||||
|
||||
Alternatively, echo.echo can be used to decorate functions. Calls to the
|
||||
decorated function will be echoed.
|
||||
|
||||
Example:
|
||||
|
||||
@echo.echo
|
||||
def my_function(args):
|
||||
pass
|
||||
"""
|
||||
import inspect
|
||||
import sys
|
||||
|
||||
|
||||
def name(item):
|
||||
" Return an item's name. "
|
||||
return item.__name__
|
||||
|
||||
|
||||
def is_classmethod(instancemethod):
|
||||
" Determine if an instancemethod is a classmethod. "
|
||||
return instancemethod.__self__ is not None
|
||||
|
||||
|
||||
def is_class_private_name(name):
|
||||
" Determine if a name is a class private name. "
|
||||
# Exclude system defined names such as __init__, __add__ etc
|
||||
return name.startswith("__") and not name.endswith("__")
|
||||
|
||||
|
||||
def method_name(method):
|
||||
""" Return a method's name.
|
||||
|
||||
This function returns the name the method is accessed by from
|
||||
outside the class (i.e. it prefixes "private" methods appropriately).
|
||||
"""
|
||||
mname = name(method)
|
||||
if is_class_private_name(mname):
|
||||
mname = "_%s%s" % (name(method.__self__.__class__), mname)
|
||||
return mname
|
||||
|
||||
|
||||
def format_arg_value(arg_val):
|
||||
""" Return a string representing a (name, value) pair.
|
||||
|
||||
>>> format_arg_value(('x', (1, 2, 3)))
|
||||
'x=(1, 2, 3)'
|
||||
"""
|
||||
arg, val = arg_val
|
||||
return "%s=%r" % (arg, val)
|
||||
|
||||
|
||||
def echo(fn, write=sys.stdout.write):
|
||||
""" Echo calls to a function.
|
||||
|
||||
Returns a decorated version of the input function which "echoes" calls
|
||||
made to it by writing out the function's name and the arguments it was
|
||||
called with.
|
||||
"""
|
||||
import functools
|
||||
# Unpack function's arg count, arg names, arg defaults
|
||||
code = fn.__code__
|
||||
argcount = code.co_argcount
|
||||
argnames = code.co_varnames[:argcount]
|
||||
fn_defaults = fn.__defaults__ or list()
|
||||
argdefs = dict(list(zip(argnames[-len(fn_defaults):], fn_defaults)))
|
||||
|
||||
@functools.wraps(fn)
|
||||
def wrapped(*v, **k):
|
||||
# Collect function arguments by chaining together positional,
|
||||
# defaulted, extra positional and keyword arguments.
|
||||
positional = list(map(format_arg_value, list(zip(argnames, v))))
|
||||
defaulted = [format_arg_value((a, argdefs[a]))
|
||||
for a in argnames[len(v):] if a not in k]
|
||||
nameless = list(map(repr, v[argcount:]))
|
||||
keyword = list(map(format_arg_value, list(k.items())))
|
||||
args = positional + defaulted + nameless + keyword
|
||||
write("%s(%s)\n" % (name(fn), ", ".join(args)))
|
||||
return fn(*v, **k)
|
||||
|
||||
return wrapped
|
||||
|
||||
|
||||
def echo_instancemethod(klass, method, write=sys.stdout.write):
|
||||
""" Change an instancemethod so that calls to it are echoed.
|
||||
|
||||
Replacing a classmethod is a little more tricky.
|
||||
See: http://www.python.org/doc/current/ref/types.html
|
||||
"""
|
||||
mname = method_name(method)
|
||||
never_echo = "__str__", "__repr__", # Avoid recursion printing method calls
|
||||
if mname in never_echo:
|
||||
pass
|
||||
elif is_classmethod(method):
|
||||
setattr(klass, mname, classmethod(echo(method.__func__, write)))
|
||||
else:
|
||||
setattr(klass, mname, echo(method, write))
|
||||
|
||||
|
||||
def echo_class(klass, write=sys.stdout.write):
|
||||
""" Echo calls to class methods and static functions
|
||||
"""
|
||||
for _, method in inspect.getmembers(klass, inspect.ismethod):
|
||||
echo_instancemethod(klass, method, write)
|
||||
for _, fn in inspect.getmembers(klass, inspect.isfunction):
|
||||
setattr(klass, name(fn), staticmethod(echo(fn, write)))
|
||||
|
||||
|
||||
def echo_module(mod, write=sys.stdout.write):
|
||||
""" Echo calls to functions and methods in a module.
|
||||
"""
|
||||
for fname, fn in inspect.getmembers(mod, inspect.isfunction):
|
||||
setattr(mod, fname, echo(fn, write))
|
||||
for _, klass in inspect.getmembers(mod, inspect.isclass):
|
||||
echo_class(klass, write)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import doctest
|
||||
|
||||
optionflags = doctest.ELLIPSIS
|
||||
doctest.testfile('echoexample.txt', optionflags=optionflags)
|
||||
doctest.testmod(optionflags=optionflags)
|
41
Sublime/fl0w/watchdog/utils/event_backport.py
Normal file
41
Sublime/fl0w/watchdog/utils/event_backport.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Backport of Event from py2.7 (method wait in py2.6 returns None)
|
||||
|
||||
from threading import Condition, Lock
|
||||
|
||||
|
||||
class Event(object):
|
||||
|
||||
def __init__(self,):
|
||||
self.__cond = Condition(Lock())
|
||||
self.__flag = False
|
||||
|
||||
def isSet(self):
|
||||
return self.__flag
|
||||
|
||||
is_set = isSet
|
||||
|
||||
def set(self):
|
||||
self.__cond.acquire()
|
||||
try:
|
||||
self.__flag = True
|
||||
self.__cond.notify_all()
|
||||
finally:
|
||||
self.__cond.release()
|
||||
|
||||
def clear(self):
|
||||
self.__cond.acquire()
|
||||
try:
|
||||
self.__flag = False
|
||||
finally:
|
||||
self.__cond.release()
|
||||
|
||||
def wait(self, timeout=None):
|
||||
self.__cond.acquire()
|
||||
try:
|
||||
if not self.__flag:
|
||||
self.__cond.wait(timeout)
|
||||
return self.__flag
|
||||
finally:
|
||||
self.__cond.release()
|
40
Sublime/fl0w/watchdog/utils/importlib2.py
Normal file
40
Sublime/fl0w/watchdog/utils/importlib2.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
# The MIT License (MIT)
|
||||
|
||||
# Copyright (c) 2013 Peter M. Elias
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE
|
||||
|
||||
|
||||
def import_module(target, relative_to=None):
|
||||
target_parts = target.split('.')
|
||||
target_depth = target_parts.count('')
|
||||
target_path = target_parts[target_depth:]
|
||||
target = target[target_depth:]
|
||||
fromlist = [target]
|
||||
if target_depth and relative_to:
|
||||
relative_parts = relative_to.split('.')
|
||||
relative_to = '.'.join(relative_parts[:-(target_depth - 1) or None])
|
||||
if len(target_path) > 1:
|
||||
relative_to = '.'.join(filter(None, [relative_to]) + target_path[:-1])
|
||||
fromlist = target_path[-1:]
|
||||
target = fromlist[0]
|
||||
elif not relative_to:
|
||||
fromlist = []
|
||||
mod = __import__(relative_to or target, globals(), locals(), fromlist)
|
||||
return getattr(mod, target, mod)
|
57
Sublime/fl0w/watchdog/utils/platform.py
Normal file
57
Sublime/fl0w/watchdog/utils/platform.py
Normal file
|
@ -0,0 +1,57 @@
|
|||
#!/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.
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
PLATFORM_WINDOWS = 'windows'
|
||||
PLATFORM_LINUX = 'linux'
|
||||
PLATFORM_BSD = 'bsd'
|
||||
PLATFORM_DARWIN = 'darwin'
|
||||
PLATFORM_UNKNOWN = 'unknown'
|
||||
|
||||
|
||||
def get_platform_name():
|
||||
if sys.platform.startswith("win"):
|
||||
return PLATFORM_WINDOWS
|
||||
elif sys.platform.startswith('darwin'):
|
||||
return PLATFORM_DARWIN
|
||||
elif sys.platform.startswith('linux'):
|
||||
return PLATFORM_LINUX
|
||||
elif sys.platform.startswith('bsd'):
|
||||
return PLATFORM_BSD
|
||||
else:
|
||||
return PLATFORM_UNKNOWN
|
||||
|
||||
__platform__ = get_platform_name()
|
||||
|
||||
|
||||
def is_linux():
|
||||
return __platform__ == PLATFORM_LINUX
|
||||
|
||||
|
||||
def is_bsd():
|
||||
return __platform__ == PLATFORM_BSD
|
||||
|
||||
|
||||
def is_darwin():
|
||||
return __platform__ == PLATFORM_DARWIN
|
||||
|
||||
|
||||
def is_windows():
|
||||
return __platform__ == PLATFORM_WINDOWS
|
64
Sublime/fl0w/watchdog/utils/unicode_paths.py
Normal file
64
Sublime/fl0w/watchdog/utils/unicode_paths.py
Normal file
|
@ -0,0 +1,64 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2013 Will Bond <will@wbond.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
from watchdog.utils import platform
|
||||
|
||||
try:
|
||||
# Python 2
|
||||
str_cls = unicode
|
||||
bytes_cls = str
|
||||
except NameError:
|
||||
# Python 3
|
||||
str_cls = str
|
||||
bytes_cls = bytes
|
||||
|
||||
|
||||
# This is used by Linux when the locale seems to be improperly set. UTF-8 tends
|
||||
# to be the encoding used by all distros, so this is a good fallback.
|
||||
fs_fallback_encoding = 'utf-8'
|
||||
fs_encoding = sys.getfilesystemencoding() or fs_fallback_encoding
|
||||
|
||||
|
||||
def encode(path):
|
||||
if isinstance(path, str_cls):
|
||||
try:
|
||||
path = path.encode(fs_encoding, 'strict')
|
||||
except UnicodeEncodeError:
|
||||
if not platform.is_linux():
|
||||
raise
|
||||
path = path.encode(fs_fallback_encoding, 'strict')
|
||||
return path
|
||||
|
||||
|
||||
def decode(path):
|
||||
if isinstance(path, bytes_cls):
|
||||
try:
|
||||
path = path.decode(fs_encoding, 'strict')
|
||||
except UnicodeDecodeError:
|
||||
if not platform.is_linux():
|
||||
raise
|
||||
path = path.decode(fs_fallback_encoding, 'strict')
|
||||
return path
|
123
Sublime/fl0w/watchdog/utils/win32stat.py
Normal file
123
Sublime/fl0w/watchdog/utils/win32stat.py
Normal file
|
@ -0,0 +1,123 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2014 Thomas Amland <thomas.amland@gmail.com>
|
||||
#
|
||||
# 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.utils.win32stat
|
||||
:synopsis: Implementation of stat with st_ino and st_dev support.
|
||||
|
||||
Functions
|
||||
---------
|
||||
|
||||
.. autofunction:: stat
|
||||
|
||||
"""
|
||||
|
||||
import ctypes
|
||||
import ctypes.wintypes
|
||||
import stat as stdstat
|
||||
from collections import namedtuple
|
||||
|
||||
|
||||
INVALID_HANDLE_VALUE = ctypes.c_void_p(-1).value
|
||||
OPEN_EXISTING = 3
|
||||
FILE_READ_ATTRIBUTES = 0x80
|
||||
FILE_ATTRIBUTE_NORMAL = 0x80
|
||||
FILE_ATTRIBUTE_READONLY = 0x1
|
||||
FILE_ATTRIBUTE_DIRECTORY = 0x10
|
||||
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
|
||||
FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
|
||||
|
||||
|
||||
class FILETIME(ctypes.Structure):
|
||||
_fields_ = [("dwLowDateTime", ctypes.wintypes.DWORD),
|
||||
("dwHighDateTime", ctypes.wintypes.DWORD)]
|
||||
|
||||
|
||||
class BY_HANDLE_FILE_INFORMATION(ctypes.Structure):
|
||||
_fields_ = [('dwFileAttributes', ctypes.wintypes.DWORD),
|
||||
('ftCreationTime', FILETIME),
|
||||
('ftLastAccessTime', FILETIME),
|
||||
('ftLastWriteTime', FILETIME),
|
||||
('dwVolumeSerialNumber', ctypes.wintypes.DWORD),
|
||||
('nFileSizeHigh', ctypes.wintypes.DWORD),
|
||||
('nFileSizeLow', ctypes.wintypes.DWORD),
|
||||
('nNumberOfLinks', ctypes.wintypes.DWORD),
|
||||
('nFileIndexHigh', ctypes.wintypes.DWORD),
|
||||
('nFileIndexLow', ctypes.wintypes.DWORD)]
|
||||
|
||||
|
||||
CreateFile = ctypes.windll.kernel32.CreateFileW
|
||||
CreateFile.restype = ctypes.wintypes.HANDLE
|
||||
CreateFile.argtypes = (
|
||||
ctypes.c_wchar_p,
|
||||
ctypes.wintypes.DWORD,
|
||||
ctypes.wintypes.DWORD,
|
||||
ctypes.c_void_p,
|
||||
ctypes.wintypes.DWORD,
|
||||
ctypes.wintypes.DWORD,
|
||||
ctypes.wintypes.HANDLE,
|
||||
)
|
||||
|
||||
GetFileInformationByHandle = ctypes.windll.kernel32.GetFileInformationByHandle
|
||||
GetFileInformationByHandle.restype = ctypes.wintypes.BOOL
|
||||
GetFileInformationByHandle.argtypes = (
|
||||
ctypes.wintypes.HANDLE,
|
||||
ctypes.wintypes.POINTER(BY_HANDLE_FILE_INFORMATION),
|
||||
)
|
||||
|
||||
CloseHandle = ctypes.windll.kernel32.CloseHandle
|
||||
CloseHandle.restype = ctypes.wintypes.BOOL
|
||||
CloseHandle.argtypes = (ctypes.wintypes.HANDLE,)
|
||||
|
||||
|
||||
StatResult = namedtuple('StatResult', 'st_dev st_ino st_mode st_mtime')
|
||||
|
||||
def _to_mode(attr):
|
||||
m = 0
|
||||
if (attr & FILE_ATTRIBUTE_DIRECTORY):
|
||||
m |= stdstat.S_IFDIR | 0o111
|
||||
else:
|
||||
m |= stdstat.S_IFREG
|
||||
if (attr & FILE_ATTRIBUTE_READONLY):
|
||||
m |= 0o444
|
||||
else:
|
||||
m |= 0o666
|
||||
return m
|
||||
|
||||
def _to_unix_time(ft):
|
||||
t = (ft.dwHighDateTime) << 32 | ft.dwLowDateTime
|
||||
return (t / 10000000) - 11644473600
|
||||
|
||||
def stat(path):
|
||||
hfile = CreateFile(path,
|
||||
FILE_READ_ATTRIBUTES,
|
||||
0,
|
||||
None,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
|
||||
None)
|
||||
if hfile == INVALID_HANDLE_VALUE:
|
||||
raise ctypes.WinError()
|
||||
info = BY_HANDLE_FILE_INFORMATION()
|
||||
r = GetFileInformationByHandle(hfile, info)
|
||||
CloseHandle(hfile)
|
||||
if not r:
|
||||
raise ctypes.WinError()
|
||||
return StatResult(st_dev=info.dwVolumeSerialNumber,
|
||||
st_ino=(info.nFileIndexHigh << 32) + info.nFileIndexLow,
|
||||
st_mode=_to_mode(info.dwFileAttributes),
|
||||
st_mtime=_to_unix_time(info.ftLastWriteTime)
|
||||
)
|
Reference in a new issue