-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 6e33799
Showing
17 changed files
with
699 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
[flake8] | ||
max-line-length = 88 | ||
ignore = E501, E203, W503 | ||
per-file-ignores = | ||
__init__.py:F401 | ||
tests/console/commands/debug/test_resolve.py:W291 | ||
exclude = | ||
.git | ||
__pycache__ | ||
setup.py | ||
build | ||
dist | ||
releases | ||
.venv | ||
.tox | ||
.mypy_cache | ||
.pytest_cache | ||
.vscode | ||
.github | ||
poetry/utils/_compat.py | ||
poetry/utils/env_scripts/tags.py | ||
tests/fixtures/ | ||
tests/repositories/fixtures/ | ||
tests/utils/fixtures/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
*.pyc | ||
|
||
# Packages | ||
*.egg | ||
!/tests/**/*.egg | ||
/*.egg-info | ||
/dist/* | ||
build | ||
_build | ||
.cache | ||
*.so | ||
|
||
# Installer logs | ||
pip-log.txt | ||
|
||
# Unit test / coverage reports | ||
.coverage | ||
.tox | ||
.pytest_cache | ||
|
||
.DS_Store | ||
.idea/* | ||
.python-version | ||
.vscode/* | ||
|
||
/test.py | ||
/test_*.* | ||
|
||
.mypy_cache | ||
|
||
.venv | ||
/releases/* | ||
pip-wheel-metadata |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
exclude: '.git|((?:[^/]*/)*)(.svg)|((?:[^/]*/)*)(.xml)' | ||
default_stages: [commit] | ||
fail_fast: true | ||
default_language_version: | ||
python: python3 # force all unspecified python hooks to run python3 | ||
repos: | ||
- repo: https://github.com/pre-commit/pre-commit-hooks | ||
rev: v3.1.0 | ||
hooks: | ||
- id: trailing-whitespace | ||
- id: end-of-file-fixer | ||
- id: check-yaml | ||
- id: check-json | ||
- id: check-added-large-files | ||
|
||
- repo: https://github.com/psf/black | ||
rev: 19.10b0 | ||
hooks: | ||
- id: black | ||
args: ['--line-length', '120'] | ||
|
||
- repo: https://gitlab.com/pycqa/flake8 | ||
rev: 3.8.1 | ||
hooks: | ||
- id: flake8 | ||
args: ['--config=setup.cfg'] | ||
language_version: python3 | ||
|
||
- repo: https://github.com/timothycrosley/isort | ||
rev: 5.0.9 | ||
hooks: | ||
- id: isort | ||
require_serial: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2021 Naveen Anil, All Rights Reserved. | ||
|
||
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from .blackwatch import application | ||
|
||
|
||
application.run() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import logging | ||
from cleo import Application | ||
|
||
from .commands import WatchCommand | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
console = logging.StreamHandler() | ||
logger.addHandler(console) | ||
|
||
|
||
class BlackWatchApplication(Application): | ||
_version = "0.1.0" | ||
|
||
|
||
application = BlackWatchApplication() | ||
application.add(WatchCommand()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .watch import WatchCommand # noqa:F401 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
from cleo import Command | ||
|
||
from ..handlers import FolderWatcher | ||
|
||
|
||
class WatchCommand(Command): | ||
""" | ||
Watch a folder | ||
watch | ||
{folder : Which folder do you want to watch?} | ||
{command? : What command to run?} | ||
{--d|debug : Debug mode?} | ||
{--k|kind=any : Whether to watch for folders or files? allowed: any,files,folders} | ||
{--i|interval=2 : How long to wait after an event for another one?} | ||
{--e|event=* : What file system even to watch for? allowed: any,create,delete,modified,moved} | ||
""" | ||
|
||
ALLOWED_KIND = ["any", "folders", "files"] | ||
ALLOWED_EVENTS = ["any", "create", "delete", "modify", "move"] | ||
|
||
def handle(self): | ||
folder = self.argument("folder") | ||
command = self.argument("command") | ||
|
||
event = self.option("event") | ||
kind = self.option("kind") | ||
interval = self.option("interval") | ||
debug = self.option("debug") | ||
|
||
try: | ||
interval = int(interval) | ||
except Exception: | ||
raise Exception("Interval cannot be converted to int, interval must be an integer") | ||
|
||
if kind not in self.ALLOWED_KIND: | ||
raise Exception("Kind must be one of 'any', 'folders' or 'files'. Default is 'any'") | ||
|
||
if "any" in event or len(event) < 1: | ||
event = ["any"] | ||
elif not all(one_event in self.ALLOWED_EVENTS for one_event in event): | ||
raise Exception("Invalid event, allowed events are", ",".join(self.ALLOWED_EVENTS)) | ||
|
||
if debug: | ||
print("folder", folder) | ||
print("command", command) | ||
print("event", event) | ||
print("kind", kind) | ||
print("interval", interval) | ||
else: | ||
if command: | ||
watcher = FolderWatcher(folder, command, interval=interval, kind=kind, event=event) | ||
else: | ||
watcher = FolderWatcher(folder, interval=interval, kind=kind, event=event) | ||
|
||
self.line( | ||
f"Watching folder: {folder} for {event} events in {kind} with interval {interval}s and running command: {command}" | ||
) | ||
watcher.run() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .fsevents import FolderWatcher # noqa:F401 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import logging | ||
import shlex | ||
import time | ||
from datetime import datetime, timedelta | ||
from subprocess import run | ||
from watchdog.events import FileSystemEventHandler | ||
from watchdog.observers import Observer | ||
|
||
|
||
logger = logging.getLogger() | ||
console = logging.StreamHandler() | ||
logger.addHandler(console) | ||
|
||
|
||
class MyHandler(FileSystemEventHandler): | ||
def __init__(self, folder, command_to_run, interval, kind, event): | ||
self._folder = folder | ||
self._command = command_to_run | ||
self._interval = interval | ||
self._kind = kind | ||
self._last_modified = datetime.now() | ||
self._event = event | ||
|
||
def should_process_event(self, event): | ||
process = False | ||
|
||
if self._kind == "any": | ||
process = True | ||
elif event.is_directory and self._kind == "folders": | ||
process = True | ||
elif self._kind == "files" and not event.is_directory: | ||
process = True | ||
|
||
return process | ||
|
||
def process_event(self, event): | ||
if self.should_process_event(event): | ||
if self._interval and (datetime.now() - self._last_modified) < timedelta(seconds=self._interval): | ||
print("skip this event, interval:", self._interval) | ||
return | ||
else: | ||
self._last_modified = datetime.now() | ||
print(f"Event type: {event.event_type} path : {event.src_path}") | ||
if self._command: | ||
try: | ||
run(shlex.split(self._command), shell=True, check=True) | ||
except Exception: | ||
raise | ||
|
||
def on_created(self, event): | ||
if "any" in self._event or "create" in self._event: | ||
self.process_event(event) | ||
|
||
def on_deleted(self, event): | ||
if "any" in self._event or "delete" in self._event: | ||
self.process_event(event) | ||
|
||
def on_modified(self, event): | ||
if "any" in self._event or "modify" in self._event: | ||
self.process_event(event) | ||
|
||
def on_moved(self, event): | ||
if "any" in self._event or "move" in self._event: | ||
self.process_event(event) | ||
|
||
|
||
class FolderWatcher: | ||
"""The class which abstracts the watching and handling of file system events | ||
:param src_path: The folder to watch | ||
:type src_path: string | ||
:param command_to_run: the command to run when a file modification is detected | ||
:type command_to_run: string | ||
""" | ||
|
||
DEFAULT_INTERVAL = 2 | ||
|
||
def __init__(self, src_path, command_to_run=None, interval=None, kind="any", event=["any"]): | ||
self.__src_path = src_path | ||
if isinstance(interval, (int)): | ||
self._interval = interval | ||
else: | ||
self._interval = self.DEFAULT_INTERVAL | ||
self.__event_handler = MyHandler(src_path, command_to_run, self._interval, kind, event) | ||
self.__event_observer = Observer() | ||
|
||
def run(self): | ||
self.start() | ||
try: | ||
while True: | ||
time.sleep(1) | ||
except KeyboardInterrupt: | ||
self.stop() | ||
|
||
def start(self): | ||
self.__schedule() | ||
self.__event_observer.start() | ||
|
||
def stop(self): | ||
self.__event_observer.stop() | ||
self.__event_observer.join() | ||
|
||
def __schedule(self): | ||
self.__event_observer.schedule(self.__event_handler, self.__src_path, recursive=True) |
Oops, something went wrong.