18 Commits
12 changed files with 62 additions and 52 deletions
-3
View File
@@ -117,6 +117,3 @@ dmypy.json
# Temporary Vim files
.*.swp
# config file
/config.py
+9 -4
View File
@@ -49,7 +49,7 @@ The basic idea is to instantiate one or multiple schedulers and map specific ino
pyinotifyd has different schedulers to schedule tasks with an optional delay. The advantages of using a scheduler are consistent logging and the possibility to cancel delayed tasks. Furthermore, schedulers have the ability to differentiate between files and directories.
### TaskScheduler
Schedule a custom python method *job* with an optional *delay* in seconds. Skip scheduling of tasks for files and/or directories according to *files* and *dirs* arguments. If there already is a scheduled task, re-schedule it with *delay*. Use *logname* in log messages.
Schedule a custom python method *job* with an optional *delay* in seconds. Skip scheduling of tasks for files and/or directories according to *files* and *dirs* arguments. If there already is a scheduled task, re-schedule it with *delay*. Use *logname* in log messages. All additional modules, functions and variables that are defined in the config file and are needed within the *job*, need to be passed as dictionary to the TaskManager through *global_vars*.
All arguments except for *job* are optional.
```python
# Please note that pyinotifyd uses pythons asyncio for asynchronous task execution.
@@ -58,6 +58,9 @@ All arguments except for *job* are optional.
# Bad: time.sleep(10)
# Good: await asyncio.sleep(10)
import asyncio
import logging
async def custom_job(event, task_id):
await asyncio.sleep(10)
logging.info(f"{task_id}: execute example task: {event}")
@@ -67,13 +70,15 @@ task_sched = TaskScheduler(
files=True,
dirs=False,
delay=0,
logname="sched")
logname="sched",
global_vars=globals())
```
### ShellScheduler
Schedule a shell command *cmd*. Replace **{maskname}**, **{pathname}** and **{src_pathname}** in *cmd* with the actual values of occuring events. This scheduler is based on TaskScheduler and has the same optional arguments.
```python
# Please note that **{src_pathname}** is only present for IN_MOVED_TO events and only in the case where the IN_MOVED_FROM events are watched too.
# Please note that **{src_pathname}** is only present for IN_MOVED_TO events and only
# in the case where the IN_MOVED_FROM events are watched too.
# If it is not present, the command line argument will be an empty string.
shell_sched = ShellScheduler(
cmd="/usr/local/bin/task.sh {maskname} {pathname} {src_pathname}")
@@ -129,7 +134,7 @@ event_map = {
"IN_Q_OVERFLOW": None,
"IN_UNMOUNT": Cancel(task_sched)}
# It is possible to instantiate an event map with a default scheduler set for every event,
# It is possible to instantiate an event map with a default scheduler set for every event
event_map = EventMap(default_sched=task_sched)
```
The following events are available:
@@ -2,7 +2,7 @@
# Distributed under the terms of the GNU General Public License v2
EAPI=7
PYTHON_COMPAT=( python3_{7,8,9} )
PYTHON_COMPAT=( python3_{8..10} )
DISTUTILS_USE_SETUPTOOLS=rdepend
SCM=""
+1 -1
View File
@@ -1,6 +1,6 @@
#!/bin/bash
set -e
set -x
PYTHON=$(which python)
script_dir=$(dirname "$(readlink -f -- "$BASH_SOURCE")")
+1
View File
@@ -7,6 +7,7 @@ script_dir=$(dirname "$(readlink -f -- "$BASH_SOURCE")")
pkg_dir=$(realpath "${script_dir}/../..")
cd "${pkg_dir}/dist"
ls -la
msg="Select version to distribute (cancel with CTRL+C):"
echo "${msg}"
select version in $(find . -maxdepth 1 -type f -name "pyinotifyd-*.*.*.tar.gz" -printf "%f\n" | sed "s#\.tar\.gz##g"); do
+12 -13
View File
@@ -36,7 +36,7 @@ from pyinotify import ProcessEvent
from pyinotifyd._install import install, uninstall
from pyinotifyd.scheduler import TaskScheduler, Cancel
__version__ = "0.0.4"
__version__ = "0.0.6"
def setLoglevel(loglevel, logname=None):
@@ -203,12 +203,12 @@ class Pyinotifyd:
config = {}
name = Pyinotifyd.name
exec("from logging import DEBUG, INFO, WARNING, ERROR, CRITICAL",
{}, config)
exec(f"from {name} import Pyinotifyd, Watch", {}, config)
exec(f"from {name} import setLoglevel, enableSyslog", {}, config)
exec(f"from {name}.scheduler import *", {}, config)
config)
exec(f"from {name} import Pyinotifyd, Watch", config)
exec(f"from {name} import setLoglevel, enableSyslog", config)
exec(f"from {name}.scheduler import *", config)
with open(config_file, "r") as fh:
exec(fh.read(), {}, config)
exec(fh.read(), config)
instance = config[f"{name}"]
assert isinstance(instance, Pyinotifyd), \
f"{name}: expected {type(Pyinotifyd)}, " \
@@ -436,15 +436,14 @@ def main():
ch.setFormatter(formatter)
loop = asyncio.get_event_loop()
for signame in ["SIGINT", "SIGTERM"]:
loop.add_signal_handler(
getattr(signal, signame),
lambda: loop.create_task(
daemon.shutdown(signame)))
signal.SIGTERM, lambda: loop.create_task(
daemon.shutdown("SIGTERM")))
loop.add_signal_handler(
getattr(signal, "SIGHUP"),
lambda: loop.create_task(
signal.SIGINT, lambda: loop.create_task(
daemon.shutdown("SIGINT")))
loop.add_signal_handler(
signal.SIGHUP, lambda: loop.create_task(
daemon.reload("SIGHUP", args.config, args.debug)))
daemon.start()
+12 -3
View File
@@ -21,14 +21,19 @@ import shutil
import sys
SYSTEMD_PATH = "/lib/systemd/system"
SYSTEMD_PATHS = ["/lib/systemd/system", "/usr/lib/systemd/system"]
OPENRC = "/sbin/openrc"
def _systemd_files(pkg_dir, name):
for path in SYSTEMD_PATHS:
if os.path.isdir(path):
break
return [
(f"{pkg_dir}/misc/systemd/{name}.service",
f"{SYSTEMD_PATH}/{name}.service", True)]
f"{path}/{name}.service", True)]
def _openrc_files(pkg_dir, name):
@@ -113,7 +118,11 @@ def _check_root():
def _check_systemd():
systemd = os.path.isdir(SYSTEMD_PATH)
for path in SYSTEMD_PATHS:
systemd = os.path.isdir(path)
if systemd:
break
if systemd:
logging.info("systemd detected")
+6 -1
View File
@@ -2,14 +2,19 @@
# TaskScheduler config #
##########################
#import asyncio
#import logging
#
#async def custom_job(event, task_id):
# asyncio.sleep(1)
# logging.info(f"{task_id}: execute example task: {event}")
#
#task_sched = TaskScheduler(
# job=custom_job,
# files=True,
# dirs=False,
# delay=10)
# delay=10
# global_vars=globals())
###########################
-5
View File
@@ -13,11 +13,6 @@ retry="SIGTERM/${shutdown_timeout}"
extra_commands="configtest reload"
depend() {
need net
before mta
}
checkconfig() {
output=$(${command} ${command_args} -t 2>&1)
ret=$?
+16 -8
View File
@@ -25,7 +25,6 @@ import os
import re
import shutil
from dataclasses import dataclass
from inspect import iscoroutinefunction
from shlex import quote as shell_quote
from uuid import uuid4
@@ -46,14 +45,14 @@ class SchedulerLogger(logging.LoggerAdapter):
class TaskScheduler:
@dataclass
class TaskState:
id: str = str(uuid4())
task: asyncio.Task = None
cancelable: bool = True
def __init__(self, task_id=None, task=None, cancelable=True):
self.id = task_id or str(uuid4())
self.task = task
self.cancelable = cancelable
def __init__(self, job, files=True, dirs=False, delay=0, logname="sched",
loop=None):
loop=None, global_vars={}):
assert iscoroutinefunction(job), \
f"job: expected coroutine, got {type(job)}"
assert isinstance(files, bool), \
@@ -62,6 +61,8 @@ class TaskScheduler:
f"dirs: expected {type(bool)}, got {type(dirs)}"
assert isinstance(delay, int), \
f"delay: expected {type(int)}, got {type(delay)}"
assert isinstance(global_vars, dict), \
f"global_vars: expected {type(dict)}, got {type(global_vars)}"
self._job = job
self._files = files
@@ -69,7 +70,7 @@ class TaskScheduler:
self._delay = delay
self._log = logging.getLogger((logname or __name__))
self._loop = (loop or asyncio.get_event_loop())
self._globals = global_vars
self._tasks = {}
self._pause = False
@@ -125,7 +126,14 @@ class TaskScheduler:
return
logger.info("start task")
if self._globals:
local_vars = {"self": self,
"event": event,
"task_id": task_state.id}
task_state.task = self._loop.create_task(
eval("self._job(event, task_id)", self._globals, local_vars))
else:
task_state.task = self._loop.create_task(
self._job(event, task_state.id))
@@ -135,7 +143,7 @@ class TaskScheduler:
except asyncio.CancelledError:
logger.warning("ongoing task cancelled")
else:
self._log.info("task finished")
logger.info("task finished")
finally:
del self._tasks[event.pathname]
+1 -1
View File
@@ -18,7 +18,7 @@ setup(name = "pyinotifyd",
# 3 - Alpha
# 4 - Beta
# 5 - Production/Stable
"Development Status :: 3 - Alpha",
"Development Status :: 5 - Production/Stable",
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
"Operating System :: OS Independent",
"Programming Language :: Python",
-9
View File
@@ -1,9 +0,0 @@
#!/usr/bin/env python
import sys
import pyinotifyd
if __name__ == '__main__':
sys.exit(
pyinotifyd.main()
)