diff --git a/docs/configuration.rst b/docs/configuration.rst index b90d5f7b..65e129b7 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -1057,8 +1057,8 @@ Description extractor.*.actions ------------------- Type - * ``object`` (`pattern` -> `action(s)`) - * ``list`` of ``lists`` with `pattern` -> `action(s)` pairs as elements + * ``object`` (`pattern` -> `Action(s)`_) + * ``list`` of ``lists`` with `pattern` -> `Action(s)`_ pairs as elements Example .. code:: json @@ -1085,51 +1085,17 @@ Example ] Description - Perform an ``action`` when logging a message matched by ``pattern``. + Perform an Action_ when logging a message matched by ``pattern``. ``pattern`` is parsed as severity level (``debug``, ``info``, ``warning``, ``error``, or integer value) - followed by an optional `Python Regular Expression `__ - separated by a colon ``:``. + followed by an optional + `Python Regular Expression `__ + separated by a colon ``:`` + Using ``*`` as `level` or leaving it empty matches logging messages of all levels (e.g. ``*:`` or ``:``). - ``action`` is parsed as action type - followed by (optional) arguments. - - It is possible to specify more than one ``action`` per ``pattern`` - by providing them as a ``list``: ``["", "", …]`` - - Supported Action Types: - - ``status``: - | Modify job exit status. - | Expected syntax is `` `` (e.g. ``= 100``). - - Supported operators are - ``=`` (assignment), - ``&`` (bitwise AND), - ``|`` (bitwise OR), - ``^`` (bitwise XOR). - ``level``: - | Modify severity level of the current logging message. - | Can be one of ``debug``, ``info``, ``warning``, ``error`` or an integer value. - ``print``: - Write argument to stdout. - ``exec``: - Run a shell command. - ``abort``: - Stop the current extractor run. - ``terminate``: - Stop the current extractor run, including parent extractors. - ``restart``: - Restart the current extractor run. - ``wait``: - | Sleep for a given Duration_ or - | wait until Enter is pressed when no argument was given. - ``exit``: - Exit the program with the given argument as exit status. - extractor.*.postprocessors -------------------------- @@ -7942,6 +7908,27 @@ Description as signal handler for. +signals-actions +--------------- +Type + ``object`` (`signal` -> `Action(s)`_) +Example + .. code:: json + + { + "SIGINT" : "flag download = stop", + "SIGUSR1": [ + "print Received SIGUSR1", + "exec notify.sh", + "exit 127" + ] + } +Description + `Action(s)`_ to perform when a + `signal `__ + is received. + + subconfigs ---------- Type @@ -8279,6 +8266,67 @@ Description Store files in a ZIP archive +Action +------ +Type + ``string`` +Example + * ``"exit"`` + * ``"print Hello World"`` + * ``"raise AbortExtraction an error occured"`` + * ``"flag file = terminate"`` +Description + An Action_ is parsed as `Action Type` + followed by (optional) arguments. + + It is possible to specify more than one ``action`` + by providing them as a ``list``: ``["", "", …]`` + + Supported `Action Types`: + + ``status``: + | Modify job exit status. + | Expected syntax is `` `` (e.g. ``= 100``). + + Supported operators are + ``=`` (assignment), + ``&`` (bitwise AND), + ``|`` (bitwise OR), + ``^`` (bitwise XOR). + ``level``: + | Modify severity level of the current logging message. + | Can be one of ``debug``, ``info``, ``warning``, ``error`` or an integer value. + ``print``: + Write argument to stdout. + ``exec``: + Run a shell command. + ``abort``: + Stop the current extractor run. + ``terminate``: + Stop the current extractor run, including parent extractors. + ``restart``: + Restart the current extractor run. + ``raise``: + Raise an exception. + + This can be an exception defined in + `exception.py `_ + or a + `built-in exception `_ + (e.g. ``ZeroDivisionError``) + ``flag``: + Set a ``flag``. + + | Expected syntax is ``[ = ]`` (e.g. ``post = stop``) + | ```` can be one of ``file``, ``post``, ``child``, ``download`` + | ```` can be one of ``stop``, ``abort``, ``terminate``, ``restart`` (default ``stop``) + ``wait``: + | Sleep for a given Duration_ or + | wait until Enter is pressed when no argument was given. + ``exit``: + Exit the program with the given argument as exit status. + + .. |ytdl| replace:: `yt-dlp`_/`youtube-dl`_ .. |ytdl's| replace:: yt-dlp's/youtube-dl's @@ -8311,6 +8359,7 @@ Description .. _deviantart.comments: `extractor.deviantart.comments`_ .. _postprocessors: `extractor.*.postprocessors`_ .. _download archive: `extractor.*.archive`_ +.. _Action(s): Action_ .. _.netrc: https://stackoverflow.com/tags/.netrc/info .. _Last-Modified: https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.29 diff --git a/gallery_dl/__init__.py b/gallery_dl/__init__.py index cb040b65..6b5cfd05 100644 --- a/gallery_dl/__init__.py +++ b/gallery_dl/__init__.py @@ -90,23 +90,8 @@ def main(): signal.signal(signal_num, signal.SIG_IGN) if signals := config.get((), "signals-actions"): - import signal - - def signals_handler(event, action): - def handler(signal_num, frame): - signal_name = signal.Signals(signal_num).name - output.stderr_write(f"{signal_name} received\n") - util.FLAGS.__dict__[event] = action - return handler - - for signal_name, action in signals.items(): - signal_num = getattr(signal, signal_name, None) - if signal_num is None: - log.warning("signal '%s' is not defined", signal_name) - else: - event, _, action = action.rpartition(":") - signal.signal(signal_num, signals_handler( - event.upper() if event else "FILE", action.lower())) + from . import actions + actions.parse_signals(signals) # enable ANSI escape sequences on Windows if util.WINDOWS and config.get(("output",), "ansi", output.COLORS): diff --git a/gallery_dl/actions.py b/gallery_dl/actions.py index 4733dd51..971c4d90 100644 --- a/gallery_dl/actions.py +++ b/gallery_dl/actions.py @@ -15,7 +15,7 @@ import functools from . import util, exception -def parse(actionspec): +def parse_logging(actionspec): if isinstance(actionspec, dict): actionspec = actionspec.items() @@ -73,6 +73,41 @@ def parse(actionspec): return actions +def parse_signals(actionspec): + import signal + + if isinstance(actionspec, dict): + actionspec = actionspec.items() + + for signal_name, spec in actionspec: + signal_num = getattr(signal, signal_name, None) + if signal_num is None: + log = logging.getLogger("gallery-dl") + log.warning("signal '%s' is not defined", signal_name) + continue + + if isinstance(spec, str): + type, _, args = spec.partition(" ") + before, after = ACTIONS[type](args) + action = before if after is None else after + else: + actions_before = [] + actions_after = [] + for s in spec: + type, _, args = s.partition(" ") + before, after = ACTIONS[type](args) + if before is not None: + actions_before.append(before) + if after is not None: + actions_after.append(after) + + actions = actions_before + actions.extend(actions_after) + action = _chain_actions(actions) + + signal.signal(signal_num, signals_handler(action)) + + class LoggerAdapter(): def __init__(self, logger, job): @@ -128,6 +163,12 @@ def _chain_actions(actions): return _chain +def signals_handler(action, args={}): + def handler(signal_num, frame): + action(args) + return handler + + # -------------------------------------------------------------------- def action_print(opts):