diff options
Diffstat (limited to 'src/mailman/app/docs/plugins.rst')
| -rw-r--r-- | src/mailman/app/docs/plugins.rst | 174 |
1 files changed, 110 insertions, 64 deletions
diff --git a/src/mailman/app/docs/plugins.rst b/src/mailman/app/docs/plugins.rst index 07d0ec33c..42038b654 100644 --- a/src/mailman/app/docs/plugins.rst +++ b/src/mailman/app/docs/plugins.rst @@ -1,10 +1,10 @@ -===== -Hooks -===== +======= +Plugins +======= -Mailman defines two initialization hooks, one which is run early in the -initialization process and the other run late in the initialization process. -Hooks name an importable callable so it must be accessible on ``sys.path``. +Mailman defines a plugin as a package accessible on ``sys.path`` that provides +components Mailman will use. Such add handlers, rules, chains, etc... +First we create an example plugin that provides one additional rule: :: >>> import os, sys @@ -12,27 +12,115 @@ Hooks name an importable callable so it must be accessible on ``sys.path``. >>> config_directory = os.path.dirname(config.filename) >>> sys.path.insert(0, config_directory) + >>> example_plugin_path = os.path.join(config_directory, 'example_plugin') + >>> rules_path = os.path.join(example_plugin_path, 'rules') + >>> os.makedirs(rules_path) + >>> open(os.path.join(example_plugin_path, '__init__.py'), 'a').close() + >>> open(os.path.join(rules_path, '__init__.py'), 'a').close() + >>> rule_path = os.path.join(rules_path, 'example_rule.py') + >>> with open(rule_path, 'w') as fp: + ... print("""\ + ... from mailman.interfaces.rules import IRule + ... from public import public + ... from zope.interface import implementer + ... + ... @public + ... @implementer(IRule) + ... class ExampleRule: + ... + ... name = 'example' + ... description = 'Example rule to show pluggable components.' + ... record = True + ... + ... def check(self, mlist, msg, msgdata): + ... return msg.original_size > 1024 + ... """, file=fp) + >>> fp.close() + +Then enable the example plugin in config. +:: + + >>> example_config = """\ + ... [plugin.example] + ... path: example_plugin + ... enable: yes + ... """ + >>> config.push('example_cfg', example_config) + +Now the `example` rule can be seen as an ``IRule`` component and will be used +when any chain uses a link called `example`. +:: + + >>> from mailman.interfaces.rules import IRule + >>> from mailman.utilities.plugins import find_pluggable_components + >>> rules = sorted([rule.name + ... for rule in find_pluggable_components('rules', IRule)]) + >>> for rule in rules: + ... print(rule) + administrivia + any + approved + banned-address + dmarc-mitigation + emergency + example + implicit-dest + loop + max-recipients + max-size + member-moderation + news-moderation + no-senders + no-subject + nonmember-moderation + suspicious-header + truth + + + >>> config.pop('example_cfg') + >>> from shutil import rmtree + >>> rmtree(example_plugin_path) + + +Hooks +===== + +Plugins can also add initialization hooks, which will be run during the +initialization process. By creating a class implementing the IPlugin interface. +One which is run early in the process and the other run late in the +initialization process. +:: + >>> hook_path = os.path.join(config_directory, 'hooks.py') >>> with open(hook_path, 'w') as fp: ... print("""\ + ... from mailman.interfaces.plugin import IPlugin + ... from public import public + ... from zope.interface import implementer + ... ... counter = 1 - ... def pre_hook(): - ... global counter - ... print('pre-hook:', counter) - ... counter += 1 ... - ... def post_hook(): - ... global counter - ... print('post-hook:', counter) - ... counter += 1 + ... @public + ... @implementer(IPlugin) + ... class ExamplePlugin: + ... + ... def pre_hook(self): + ... global counter + ... print('pre-hook:', counter) + ... counter += 1 + ... + ... def post_hook(self): + ... global counter + ... print('post-hook:', counter) + ... counter += 1 ... """, file=fp) >>> fp.close() +Running the hooks +----------------- -Pre-hook -======== - -We can set the pre-hook in the configuration file. +We can set the plugin class in the config file. +:: >>> config_path = os.path.join(config_directory, 'hooks.cfg') >>> with open(config_path, 'w') as fp: @@ -40,8 +128,9 @@ We can set the pre-hook in the configuration file. ... [meta] ... extends: test.cfg ... - ... [mailman] - ... pre_hook: hooks.pre_hook + ... [plugin.hook_example] + ... class: hooks.ExamplePlugin + ... enable: yes ... """, file=fp) The hooks are run in the second and third steps of initialization. However, @@ -72,50 +161,7 @@ script that will produce no output to force the hooks to run. >>> call() pre-hook: 1 + post-hook: 2 <BLANKLINE> >>> os.remove(config_path) - - -Post-hook -========= - -We can set the post-hook in the configuration file. -:: - - >>> with open(config_path, 'w') as fp: - ... print("""\ - ... [meta] - ... extends: test.cfg - ... - ... [mailman] - ... post_hook: hooks.post_hook - ... """, file=fp) - - >>> call() - post-hook: 1 - <BLANKLINE> - - >>> os.remove(config_path) - - -Running both hooks -================== - -We can set the pre- and post-hooks in the configuration file. -:: - - >>> with open(config_path, 'w') as fp: - ... print("""\ - ... [meta] - ... extends: test.cfg - ... - ... [mailman] - ... pre_hook: hooks.pre_hook - ... post_hook: hooks.post_hook - ... """, file=fp) - - >>> call() - pre-hook: 1 - post-hook: 2 - <BLANKLINE> |
