summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Cuthbert <tcuthbert90@gmail.com>2023-07-11 15:46:31 +0800
committerChristian Geier <geier@lostpackets.de>2024-04-11 21:19:43 +0200
commit692d2d1fa109e37b74a68cd9855f62352c3f76a7 (patch)
tree167575bcc46972e90cdc2b460287064ea595e33a
parentb38de1843be20f741f43237adb24ffdf3cc88f66 (diff)
wip: basic plugin handling
-rw-r--r--khal/_compat.py8
-rw-r--r--khal/cli.py5
-rw-r--r--khal/khalendar/event.py10
-rw-r--r--khal/khalendar/typing.py15
-rw-r--r--khal/plugins.py54
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()