From f262d9f779292593cf043cc01b15ef785480bdd9 Mon Sep 17 00:00:00 2001 From: mathieui Date: Sat, 13 Apr 2013 22:56:00 +0200 Subject: [PATCH] Add the last pages, and the plugin examples --- doc/source/dev/contributing.rst | 67 +++++++++++++++++++ doc/source/dev/index.rst | 17 +++-- doc/source/dev/overview.rst | 114 ++++++++++++++++++++++++++++++++ doc/source/dev/plugin.rst | 31 +++++++++ 4 files changed, 225 insertions(+), 4 deletions(-) create mode 100644 doc/source/dev/contributing.rst create mode 100644 doc/source/dev/overview.rst diff --git a/doc/source/dev/contributing.rst b/doc/source/dev/contributing.rst new file mode 100644 index 00000000..ca7de049 --- /dev/null +++ b/doc/source/dev/contributing.rst @@ -0,0 +1,67 @@ +Contributing +============ + +Conventions +----------- + +We don’t have a strict set of conventions, but you should respect PEP8 mostly +(e.g. 4 spaces, class names in CamelCase and methods lowercased with +underscores) except if it means less-readable code (80 chars is often a hassle, +and if you look inside poezio you’ll see lots of long lines, mostly because of +strings). + +As explained in the :ref:`overview`, “global” code goes in +:file:`core.py`, tab-related code goes in :file:`tabs.py`, and ui-related code goes in +:file:`windows.py`. There are other modules (e.g. :file:`xhtml.py`) but they do not matter +for the application as a whole. + +Commit guidelines +----------------- + +Commits **should** have a meaninful title (first line), and *may* have a detailed +description below. There are of course exceptions (for example, a single-line +commit that takes care of a typo right behind a big commit does not need to +say ``fix a typo ("azre" → "are") in toto.py line 45454``, since the metainfos +already take care of that.), but if you do not have commit access on the +poezio trunk, you can still reset and commit again. + + +Try to do atomic commits: since git is a DVCS, it doesn’t hurt to ``git add -p`` +and split the commit into several meaningful small commits ; on the contrary, +it helps to track the changes on different levels. + + +If you have a conflict, solve it with rebase and not merge if the fast-forwards +do not resolve it automatically in your case. This helps to avoid creating +useless merges (and polluting the commit history) when none is needed. + +.. code-block:: bash + + git fetch origin + git rebase origin/master + git push origin master + +If your commit is related to an issue on our tracker_ (or fixes such an +issue), you can use ``Fix #BUGID`` or ``References #BUGID`` to help with the +tracking. + + +Getting your code into poezio +----------------------------- + +If you have code you want to contribute, you can: + +* Give us a patch and a description of what it does +* Give us a link to a **git** repo from which we can pull + +The code is of course reviewed and tested a bit, but we trust the contributors +to submit good code. If we can’t integrate the given code into poezio (if it +crashes or has some issues), if the size is small, we may tweak it ourselves +and integrate it, and if not, you are of course free to take our advice into +account and submit it again. + + +If you have already submitted some code and plan to do more, you can ask us + direct commit access on the main repo. + +.. _tracker: https://dev.louiz.org/project/poezio/bugs diff --git a/doc/source/dev/index.rst b/doc/source/dev/index.rst index 8bf674cc..aae0a6d0 100644 --- a/doc/source/dev/index.rst +++ b/doc/source/dev/index.rst @@ -6,16 +6,25 @@ Development documentation ========================= -Contents: + +About plugins +------------- .. toctree:: :maxdepth: 2 plugin events - timed_events - common sleek - theming xep +About Poezio +------------ + +.. toctree:: + + overview + contributing + theming + timed_events + common diff --git a/doc/source/dev/overview.rst b/doc/source/dev/overview.rst new file mode 100644 index 00000000..452938e6 --- /dev/null +++ b/doc/source/dev/overview.rst @@ -0,0 +1,114 @@ +.. _overview: + +Overview +======== + +.. note:: This is not an introduction to XMPP, but to how poezio works. + + +Global overview +--------------- + +Poezio is an application that has three main layers, mostly separated in three +different python modules: ``core``, ``tabs``, and ``windows``. An UML diagram of +Poezio would be inneficient, cluttered, or incomplete, so there is none, if +that bugs you. + +.. figure:: ../images/layers.png + :alt: Layers + +**Core** is mostly a “global” object containing the state of the application at +any time, it contains the global commands, the xmpp event handlers, the list +of open tabs, etc. Most objects in poezio have a self.core attribute +referencing the **Core** (it’s a singleton, so there is never more than one +instance). **Core** also contains the main loop of the application, which then +dispatchs the I/O events (keypress) to the appropriate methods. + +But the main loop is not the most important thing in poezio; because it is an +IM client, it is essentially event-driven. The event part is handled by +SleekXMPP, which is the library we chose after moving away from xmpppy. + + +**Tabs** are the second layer of poezio, but the first dealing with the UI: each +**Tab** is a layout of several **windows**, it contains tab-specific commands, +tab-specific keybinds, and it has methods in order for core to +interact with it, and some methods are only proxies for the methods of a +**window**. + +Example scenario: If someone presses the key PageUp, then Core will call the +appropriate method on the current _Tab_, which will in turn, if it implements the +method (inherited empty from the Tab class), call a scrolling method from the +appropriate **window**. + +All tabs types inherit from the class **Tab**, and the tabs featuring +chat functionnality will inherit from **ChatTab** (which inherits from **Tab**). + +Examples of **tabs**: MUCTab, XMLTab, RosterTab, MUCListTab, etc… + +Event handlers +-------------- + +The events handlers are registered right at the start of poezio, and then +when a matching stanza is received, the handler is called in a separate thread +from the main loop. The handlers are in **Core**, and then they call the +appropriate methods in the corresponding **tabs**. + +Example scenario: if a message is received from a MUC, then the **Core** handler +will identify the **Tab**, and call the relevant handler from this **Tab**, this tab +will in turn, add the message to the buffer, which will then add it to the +relevant **windows**. + +.. note:: All the _windows_ that deal with received or generated text are linked + to a **text_buffer**, in order to rebuild all the display lines from the + sources if necessary. This also enables us to have several **windows** + presenting the same text, even if they are not of the same size and layout. + +Commands and completion +----------------------- + +Commands are quite straightforward: those are methods that take a string as a +parameter, and they do stuff. + +From an user point of view, the methods are entered like that: + +.. code-block:: none + + /command arg1 arg2 + +or + +.. code-block:: none + + /command "arg1 with spaces" arg2 + +However, when creating a command, you wil deal with _one_ str, no matter what. +There are utilities to deal with it (common.shell_split), but it is not always +necessary. Commands are registered in the **commands** dictionnary of a tab +structured as key (command name) -> tuple(command function, help string, completion). + + +Completions are a bit tricky, but it’s easy once you get used to it: + +They take an **Input** (a _windows_ class) as a parameter, named the_input +everywhere in the sources. To effectively have a completion, you have to call +**the_input.auto_completion()** at the end of the function. + + +.. code-block:: python + + class Input(Win): + # … + def auto_completion(completion_list, after='', quote=True): + # … + +Set the input to iterate over _completion_list_ when the user hits tab, insert +**after** after the completed item, and surround the item with double quotes or +not. + +There is no method to find the current argument in the input (although the +feature is planned), so you have to assume the current argument is the last, +and guess it by splitting the string an checking for end-space. + +You can look for examples in the sources, all the possible cases are +covered (single-argument, complex arguments with spaces, several arguments, +etc…) diff --git a/doc/source/dev/plugin.rst b/doc/source/dev/plugin.rst index 7978a5e9..323a058a 100644 --- a/doc/source/dev/plugin.rst +++ b/doc/source/dev/plugin.rst @@ -1,6 +1,9 @@ Plugin API documentation ======================== +BasePlugin +---------- + .. module:: plugin .. autoclass:: BasePlugin @@ -34,8 +37,36 @@ The :py:class:`~PluginAPI` object is an a interface through which the :py:class: (and inheritors) *should* go to interact with poezio. If it is not sufficient, then the ``core`` member can be used. +PluginAPI +--------- + .. autoclass:: PluginAPI :members: :undoc-members: +Example plugins +--------------- + +**Example 1:** Add a simple command that sends "Hello World!" into the conversation + +.. code-block:: python + + class Plugin(BasePlugin): + def init(self): + self.add_command('hello', self.command_hello, "Send 'Hello World!'") + + def command_hello(self, arg): + self.core.send_message('Hello World!') + +**Example 2:** Adds an event handler that sends “tg” to a groupchat when a message is received from someone named “Partauche” + +.. code-block:: python + + class Plugin(BasePlugin): + def init(self): + self.add_event_handler('muc_msg', self.on_groupchat_message) + + def on_groupchat_message(self, message, tab): + if message['mucnick'] == "Partauche": + tab.command_say('tg')