Add the last pages, and the plugin examples

This commit is contained in:
mathieui 2013-04-13 22:56:00 +02:00
parent d676c2ee7b
commit f262d9f779
4 changed files with 225 additions and 4 deletions

View file

@ -0,0 +1,67 @@
Contributing
============
Conventions
-----------
We dont 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 youll 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 doesnt 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 cant 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

View file

@ -6,16 +6,25 @@
Development documentation Development documentation
========================= =========================
Contents:
About plugins
-------------
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
plugin plugin
events events
timed_events
common
sleek sleek
theming
xep xep
About Poezio
------------
.. toctree::
overview
contributing
theming
timed_events
common

114
doc/source/dev/overview.rst Normal file
View file

@ -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** (its 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 its 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…)

View file

@ -1,6 +1,9 @@
Plugin API documentation Plugin API documentation
======================== ========================
BasePlugin
----------
.. module:: plugin .. module:: plugin
.. autoclass:: BasePlugin .. 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`` (and inheritors) *should* go to interact with poezio. If it is not sufficient, then the ``core``
member can be used. member can be used.
PluginAPI
---------
.. autoclass:: PluginAPI .. autoclass:: PluginAPI
:members: :members:
:undoc-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')