diff options
author | Thomas Cuthbert <tcuthbert90@gmail.com> | 2023-07-11 15:46:31 +0800 |
---|---|---|
committer | Christian Geier <geier@lostpackets.de> | 2024-04-11 21:19:43 +0200 |
commit | 692d2d1fa109e37b74a68cd9855f62352c3f76a7 (patch) | |
tree | 167575bcc46972e90cdc2b460287064ea595e33a | |
parent | b38de1843be20f741f43237adb24ffdf3cc88f66 (diff) |
wip: basic plugin handling
-rw-r--r-- | khal/_compat.py | 8 | ||||
-rw-r--r-- | khal/cli.py | 5 | ||||
-rw-r--r-- | khal/khalendar/event.py | 10 | ||||
-rw-r--r-- | khal/khalendar/typing.py | 15 | ||||
-rw-r--r-- | khal/plugins.py | 54 |
5 files changed, 90 insertions, 2 deletions
diff --git a/khal/_compat.py b/khal/_compat.py new file mode 100644 index 00000000..f0914f7e --- /dev/null +++ b/khal/_compat.py @@ -0,0 +1,8 @@ +__all__ = ("importlib_metadata", "Protocol", "Literal", "tomllib") + +import sys + +if sys.version_info >= (3, 10): # pragma: no cover + from importlib import metadata as importlib_metadata +else: # pragma: no cover + import importlib_metadata diff --git a/khal/cli.py b/khal/cli.py index ef7dc5df..8fc2f14c 100644 --- a/khal/cli.py +++ b/khal/cli.py @@ -30,7 +30,7 @@ from shutil import get_terminal_size import click import click_log -from . import __version__, controllers, khalendar +from . import __version__, controllers, khalendar, plugins from .exceptions import FatalError from .settings import InvalidSettingsError, NoConfigFile, get_config from .terminal import colored @@ -323,6 +323,9 @@ def _get_cli(): daterange, once, notstarted, json, format, day_format): """List all events between a start (default: today) and (optional) end datetime.""" + enabled_eventformatters = plugins.FORMATTERS + logger.warn("TODO: register user given format string as a plugin") + logger.debug(f'{enabled_eventformatters}') try: event_column = controllers.khal_list( build_collection( diff --git a/khal/khalendar/event.py b/khal/khalendar/event.py index 9f733952..c3112fba 100644 --- a/khal/khalendar/event.py +++ b/khal/khalendar/event.py @@ -38,6 +38,7 @@ from ..custom_types import LocaleConfiguration from ..exceptions import FatalError from ..icalendar import cal_from_ics, delete_instance, invalid_timezone from ..parse_datetime import timedelta2str +from ..plugins import FORMATTERS from ..utils import generate_random_uid, is_aware, to_naive_utc, to_unix_time logger = logging.getLogger('khal') @@ -725,7 +726,14 @@ class Event: attributes["partstat-symbol"] = self._partstat_str attributes["title"] = self.summary attributes["organizer"] = self.organizer.strip() - attributes["description"] = self.description.strip() + + formatters = FORMATTERS.values() + if len(formatters) == 1: + fmt = list(formatters)[0] + else: + def fmt(s): return s.strip() + + attributes["description"] = fmt(self.description) attributes["description-separator"] = "" if attributes["description"]: attributes["description-separator"] = " :: " diff --git a/khal/khalendar/typing.py b/khal/khalendar/typing.py new file mode 100644 index 00000000..b863c7e5 --- /dev/null +++ b/khal/khalendar/typing.py @@ -0,0 +1,15 @@ +from typing import TYPE_CHECKING, Any, Callable + +if TYPE_CHECKING: + from khal.khalendar.event import Event + +# There should be `khal.khalendar.Event` instead of `Any` +# here and in `Postprocess` below but that results in recursive typing +# which mypy doesn't support until +# https://github.com/python/mypy/issues/731 is implemented. +Render = Callable[ + ["Event", Any], + str, +] + +Postprocess = Callable[[str, "Event", Any], str] diff --git a/khal/plugins.py b/khal/plugins.py new file mode 100644 index 00000000..891eca99 --- /dev/null +++ b/khal/plugins.py @@ -0,0 +1,54 @@ +from collections.abc import Callable, Mapping + +from khal._compat import importlib_metadata + +# from khal._compat import Protocol, importlib_metadata +# from khal.khalendar.typing import Postprocess, Render + +# This is a shameless ripoff of mdformat's plugin extension API. +# see: +# https://github.com/executablebooks/mdformat/blob/master/src/mdformat/plugins.py +# https://setuptools.pypa.io/en/latest/userguide/entry_point.html + + +def _load_formatters() -> dict[str, Callable[[str], str]]: + formatter_entrypoints = importlib_metadata.entry_points(group="khal.formatter") + return {ep.name: ep.load() for ep in formatter_entrypoints} + + +FORMATTERS: Mapping[str, Callable[[str], str]] = _load_formatters() + +# class ParserExtensionInterface(Protocol): +# """An interface for parser extension plugins.""" + +# # Does the plugin's formatting change Markdown AST or not? +# # (optional, default: False) +# CHANGES_AST: bool = False + +# # A mapping from `RenderTreeNode.type` to a `Render` function that can +# # render the given `RenderTreeNode` type. These override the default +# # `Render` funcs defined in `mdformat.renderer.DEFAULT_RENDERERS`. +# RENDERERS: Mapping[str, Render] + +# # A mapping from `RenderTreeNode.type` to a `Postprocess` that does +# # postprocessing for the output of the `Render` function. Unlike +# # `Render` funcs, `Postprocess` funcs are collaborative: any number of +# # plugins can define a postprocessor for a syntax type and all of them +# # will run in series. +# # (optional) +# POSTPROCESSORS: Mapping[str, Postprocess] + +# @staticmethod +# def add_cli_options(parser: argparse.ArgumentParser) -> None: +# """Add options to the khal CLI, to be stored in mdit.options["mdformat"] +# (optional)""" + + +# def _load_parser_extensions() -> dict[str, ParserExtensionInterface]: +# parser_extension_entrypoints = importlib_metadata.entry_points( +# group="khal.parser_extension" +# ) +# return {ep.name: ep.load() for ep in parser_extension_entrypoints} + + +# PARSER_EXTENSIONS: Mapping[str, ParserExtensionInterface] = _load_parser_extensions() |