diff --git a/python/doc/examples.rst b/python/doc/examples.rst index 94e15e754..9a489f783 100644 --- a/python/doc/examples.rst +++ b/python/doc/examples.rst @@ -14,10 +14,11 @@ For example you can type ``python`` and then:: # instantiate and configure deltachat account import deltachat ac = deltachat.Account("/tmp/db") + ac.set_config("addr", "test2@hq5.merlinux.eu") + ac.set_config("mail_pwd", "some password") - # start configuration activity and smtp/imap threads - ac.start_threads() - ac.configure(addr="test2@hq5.merlinux.eu", mail_pw="********") + # start the IO threads and perform configuration + ac.start() # create a contact and send a message contact = ac.create_contact("someother@email.address") diff --git a/python/doc/index.rst b/python/doc/index.rst index 1c27b9317..fb93a56d4 100644 --- a/python/doc/index.rst +++ b/python/doc/index.rst @@ -4,8 +4,9 @@ deltachat python bindings The ``deltachat`` Python package provides two layers of bindings for the core Rust-library of the https://delta.chat messaging ecosystem: -- :doc:`api` is a high level interface to deltachat-core which aims - to be memory safe and thoroughly tested through continous tox/pytest runs. +- :doc:`api` is a high level interface to deltachat-core. + +- :doc:`plugins` is a brief introduction into implementing plugin hooks. - :doc:`lapi` is a lowlevel CFFI-binding to the `Rust Core `_. diff --git a/python/doc/plugins.rst b/python/doc/plugins.rst new file mode 100644 index 000000000..281669991 --- /dev/null +++ b/python/doc/plugins.rst @@ -0,0 +1,27 @@ + +Implementing Plugin Hooks +========================== + +The Delta Chat Python bindings use `pluggy `_ +for managing global and per-account plugin registration, and performing +hook calls. + + +.. autoclass:: deltachat.register_global_plugin + +.. autoclass:: deltachat.account.Account.add_account_plugin + + +Per-Account Hook specifications +------------------------------- + +.. autoclass:: deltachat.hookspec.PerAccount + :members: + + +Global Hook specifications +-------------------------- + +.. autoclass:: deltachat.hookspec.Global + :members: + diff --git a/python/src/deltachat/__init__.py b/python/src/deltachat/__init__.py index 2e52cbc46..43c100700 100644 --- a/python/src/deltachat/__init__.py +++ b/python/src/deltachat/__init__.py @@ -78,6 +78,9 @@ def get_dc_event_name(integer, _DC_EVENTNAME_MAP={}): def register_global_plugin(plugin): + """ Register a global plugin which implements one or more + of the :class:`deltachat.hookspec.Global` specs. + """ gm = hookspec.Global._get_plugin_manager() gm.register(plugin) gm.check_pending() diff --git a/python/src/deltachat/account.py b/python/src/deltachat/account.py index 946a515b7..d9a14d077 100644 --- a/python/src/deltachat/account.py +++ b/python/src/deltachat/account.py @@ -517,7 +517,9 @@ class Account(object): # def add_account_plugin(self, plugin): - """ add an account plugin whose hookimpls are called. """ + """ add an account plugin which implements one or more of + the :class:`deltachat.hookspec.PerAccount` specs. + """ self._pm.register(plugin) self._pm.check_pending() return plugin diff --git a/python/src/deltachat/hookspec.py b/python/src/deltachat/hookspec.py index da99a1660..15bf7eb94 100644 --- a/python/src/deltachat/hookspec.py +++ b/python/src/deltachat/hookspec.py @@ -15,7 +15,7 @@ global_hookimpl = pluggy.HookimplMarker(_global_name) class PerAccount: """ per-Account-instance hook specifications. - Account hook implementations need to be registered with an Account instance. + If you write a plugin you need to implement one of the following hooks. """ @classmethod def _make_plugin_manager(cls): @@ -27,8 +27,8 @@ class PerAccount: def process_ffi_event(self, ffi_event): """ process a CFFI low level events for a given account. - ffi_event has "name", "data1", "data2" attributes according - to https://c.delta.chat/group__DC__EVENT.html + ffi_event has "name", "data1", "data2" values as specified + with `DC_EVENT_* `_. """ @account_hookspec @@ -47,6 +47,10 @@ class PerAccount: def process_message_delivered(self, message): """ Called when an outgoing message has been delivered to SMTP. """ + @account_hookspec + def member_added(self, chat, contact): + """ Called for each contact added to a chat. """ + class Global: """ global hook specifications using a per-process singleton plugin manager instance. diff --git a/python/tests/conftest.py b/python/tests/conftest.py index ea545012d..5e0129cab 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -9,6 +9,7 @@ from deltachat import Account from deltachat.tracker import ConfigureTracker from deltachat import const from deltachat.capi import lib +from deltachat.hookspec import PerAccount from deltachat.eventlogger import FFIEventLogger from _pytest.monkeypatch import MonkeyPatch from ffi_event import FFIEventTracker @@ -290,6 +291,18 @@ def lp(): return Printer() +@pytest.fixture +def make_plugin_recorder(): + def make_plugin_recorder(account): + class HookImpl: + def __init__(self): + self.calls_member_added = [] + + @account_hookimpl + def member_added(self, chat, member): + self.calls_member_added.append(dict(chat=chat, member=member)) + + def wait_configuration_progress(account, min_target, max_target=1001): min_target = min(min_target, max_target) while 1: diff --git a/python/tests/test_account.py b/python/tests/test_account.py index 1b16238ce..c1a665cc4 100644 --- a/python/tests/test_account.py +++ b/python/tests/test_account.py @@ -167,6 +167,17 @@ class TestOfflineChat: else: pytest.fail("could not find chat") + def test_add_member_event(self, ac1): + contact1 = ac1.create_contact("some1@hello.com", name="some1") + contact2 = ac1.create_contact("some2@hello.com", name="some2") + chat = ac1.create_group_chat(name="title1") + + with make_plugin_recorder(ac1) as rec: + chat.add_contact(contact2) + kwargs = rec.get_first("member_added") + assert kwargs["chat"] == chat + assert kwargs["member"] == contact2 + def test_group_chat_creation(self, ac1): contact1 = ac1.create_contact("some1@hello.com", name="some1") contact2 = ac1.create_contact("some2@hello.com", name="some2")