Commit dbde08a5 authored by mathieui's avatar mathieui

Document with sphinx timed_events, common, and add methods to PluginAPI

- add methods related to timed events to the PluginAPI
- remove parse_command_args_to_alias because str.format does that, and
  better
→ update the alias plugin
parent 0a2bd90c
......@@ -6,7 +6,7 @@ Allows the creation and the removal of personal aliases.
from plugin import BasePlugin
import common
from common import parse_command_args_to_alias as parse
from common import shell_split
class Plugin(BasePlugin):
def init(self):
......@@ -27,18 +27,18 @@ class Plugin(BasePlugin):
"""
arg = common.shell_split(line)
if len(arg) < 2:
self.core.information('Alias: Not enough parameters', 'Error')
self.api.information('Alias: Not enough parameters', 'Error')
return
alias = arg[0]
command = arg[1]
tmp_args = arg[2] if len(arg) > 2 else ''
if alias in self.core.commands or alias in self.commands:
self.core.information('Alias: command already exists', 'Error')
self.api.information('Alias: command already exists', 'Error')
return
self.commands[alias] = lambda arg: self.get_command(command)(parse(arg, tmp_args))
self.commands[alias] = lambda arg: self.get_command(command)(tmp_args.format(*shell_split(arg)))
self.add_command(alias, self.commands[alias], 'This command is an alias for /%s %s' %( command, tmp_args))
self.core.information('Alias /%s successfuly created' % alias, 'Info')
self.api.information('Alias /%s successfuly created' % alias, 'Info')
def command_unalias(self, alias):
"""
......@@ -47,7 +47,7 @@ class Plugin(BasePlugin):
if alias in self.commands:
del self.commands[alias]
self.del_command(alias)
self.core.information('Alias /%s successfuly deleted' % alias, 'Info')
self.api.information('Alias /%s successfuly deleted' % alias, 'Info')
def completion_unalias(self, the_input):
aliases = [alias for alias in self.commands]
......@@ -61,6 +61,6 @@ class Plugin(BasePlugin):
pass
if name in self.core.commands:
return self.core.commands[name][0]
elif name in self.core.current_tab().commands:
return self.core.current_tab().commands[name][0]
elif name in self.api.current_tab().commands:
return self.api.current_tab().commands[name][0]
return dummy
......@@ -6,7 +6,7 @@
# it under the terms of the zlib license. See the COPYING file.
"""
various useful functions
Various useful functions.
"""
from datetime import datetime, timedelta
......@@ -29,6 +29,13 @@ ROOM_STATE_HL = 13
def get_base64_from_file(path):
"""
Convert the content of a file to base64
:param str path: The path of the file to convert.
:return: A tuple of (encoded data, mime type, sha1 hash) if
the file exists and does not exceeds the upper size limit of 16384.
:return: (None, None, error message) if it fails
:rtype: :py:class:`tuple`
"""
if not os.path.isfile(path):
return (None, None, "File does not exist")
......@@ -44,7 +51,11 @@ def get_base64_from_file(path):
def get_output_of_command(command):
"""
Runs a command and returns its output
Runs a command and returns its output.
:param str command: The command to run.
:return: The output or None
:rtype: :py:class:`str`
"""
try:
return subprocess.check_output(command.split()).decode('utf-8').split('\n')
......@@ -53,9 +64,14 @@ def get_output_of_command(command):
def is_in_path(command, return_abs_path=False):
"""
Return True if 'command' is found in one of the directories in the user's
path. If 'return_abs_path' is True, return the absolute path of the first
found command instead. Return False otherwise and on errors
Check if *command* is in the $PATH or not.
:param str command: The command to be checked.
:param bool return_abs_path: Return the absolute path of the command instead
of True if the command is found.
:return: True if the command is found, the command path if the command is found
and *return_abs_path* is True, otherwise False.
"""
for directory in os.getenv('PATH').split(os.pathsep):
try:
......@@ -95,6 +111,8 @@ def get_os_info():
"""
Returns a detailed and well formated string containing
informations about the operating system
:rtype: str
"""
if os.name == 'posix':
executable = 'lsb_release'
......@@ -151,14 +169,21 @@ def get_os_info():
def datetime_tuple(timestamp):
"""
Convert timestamp using strptime and the format: %Y%m%dT%H:%M:%S
Convert a timestamp using strptime and the format: %Y%m%dT%H:%M:%S.
Because of various datetime formats are used the following exceptions
Because various datetime formats are used, the following exceptions
are handled:
- Optional milliseconds appened to the string are removed
- Optional Z (that means UTC) appened to the string are removed
- XEP-082 datetime strings have all '-' cahrs removed to meet
the above format.
* Optional milliseconds appened to the string are removed
* Optional Z (that means UTC) appened to the string are removed
* XEP-082 datetime strings have all '-' chars removed to meet the above format.
:param str timestamp: The string containing the formatted date.
:return: The date.
:rtype: :py:class:`datetime.datetime`
>>> common.datetime_tuple('20130226T06:23:12')
datetime.datetime(2013, 2, 26, 8, 23, 12)
"""
timestamp = timestamp.split('.')[0]
timestamp = timestamp.replace('-', '')
......@@ -172,6 +197,17 @@ def datetime_tuple(timestamp):
return ret
def shell_split(st):
"""
Split a string correctly according to the quotes
around the elements.
:param str st: The string to split.
:return: A list of the different of the string.
:rtype: :py:class:`list`
>>> shell_split('"sdf 1" "toto 2"')
['sdf 1', 'toto 2']
"""
sh = shlex.shlex(st, posix=True)
sh.commenters = ''
sh.whitespace_split = True
......@@ -188,7 +224,12 @@ def shell_split(st):
def parse_str_to_secs(duration=''):
"""
Parse a string of with a number of d, h, m, s
Parse a string of with a number of d, h, m, s.
:param str duration: The formatted string.
:return: The number of seconds represented by the string
:rtype: :py:class:`int`
>>> parse_str_to_secs("1d3m1h")
90180
"""
......@@ -210,8 +251,15 @@ def parse_str_to_secs(duration=''):
def parse_secs_to_str(duration=0):
"""
Do the reverse operation of :py:func:`parse_str_to_secs`.
Parse a number of seconds to a human-readable string.
The string has the form XdXhXmXs. 0 units are removed.
:param int duration: The duration, in seconds.
:return: A formatted string containing the duration.
:rtype: :py:class:`str`
>>> parse_secs_to_str(3601)
1h1s
"""
......@@ -230,36 +278,13 @@ def parse_secs_to_str(duration=0):
result = '0s'
return result
def parse_command_args_to_alias(arg, strto):
"""
Parse command parameters.
Numbers can be from 0 to 9.
>>> parse_command_args_to_alias('sdf koin', '%1 %0')
"koin sdf"
def safeJID(*args, **kwargs):
"""
if '%' not in strto:
return strto + arg
args = shell_split(arg)
l = len(args)
dest = ''
var_num = False
for i in strto:
if i != '%':
if not var_num:
dest += i
elif i in string.digits:
if 0 <= int(i) < l:
dest += args[int(i)]
var_num = False
elif i == '%':
if var_num:
dest += '%'
var_num = False
else:
var_num = True
return dest
Construct a :py:class:`sleekxmpp.JID` object from a string.
def safeJID(*args, **kwargs):
Used to avoid tracebacks during is stringprep fails
(fall back to a JID with an empty string).
"""
try:
return JID(*args, **kwargs)
except InvalidJID:
......
......@@ -6,6 +6,7 @@ These are used in the plugin system added in poezio 0.7.5
import os
from functools import partial
from configparser import RawConfigParser
from timed_events import TimedEvent, DelayedEvent
import config
import inspect
import traceback
......@@ -120,12 +121,49 @@ class PluginAPI(object):
def add_timed_event(self, _, *args, **kwargs):
"""
Add a timed event.
Schedule a timed event.
:param event: The timed event to add.
:param timed_events.TimedEvent event: The timed event to schedule.
"""
return self.core.add_timed_event(*args, **kwargs)
def remove_timed_event(self, _, *args, **kwargs):
"""
Unschedule a timed event.
:param timed_events.TimedEvent event: The event to unschedule.
"""
return self.core.remove_timed_event(*args, **kwargs)
def create_timed_event(self, _, *args, **kwargs):
"""
Create a timed event, but do not schedule it;
:py:func:`~PluginAPI.add_timed_event` must be used for that.
:param datetime.datetime date: The time at which the handler must be executed
:param function callback: The handler that will be executed
:param \*args: Optional arguments passed to the handler.
:return: The created event.
:rtype: :py:class:`timed_events.TimedEvent`
"""
return TimedEvent(*args, **kwargs)
def create_delayed_event(self, _, *args, **kwargs):
"""
Create a delayed event, but do not schedule it;
:py:func:`~PluginAPI.add_timed_event` must be used for that.
A delayed event is a timed event with a delay from the time
this function is called (instead of a datetime).
:param int delay: The number of seconds to schedule the execution
:param function callback: The handler that will be executed
:param \*args: Optional arguments passed to the handler.
:return: The created event.
:rtype: :py:class:`timed_events.DelayedEvent`
"""
return DelayedEvent(*args, **kwargs)
def information(self, _, *args, **kwargs):
"""
Display a new message in the information buffer.
......@@ -139,7 +177,7 @@ class PluginAPI(object):
"""
Get the current Tab.
:returns: tabs.Tab The current tab.
:returns: The current tab.
"""
return self.core.current_tab()
......@@ -176,7 +214,7 @@ class PluginAPI(object):
Example string: "<server> [port]"
:raises: Exception If the command already exists.
:raises Exception: If the command already exists.
"""
return self.plugin_manager.add_command(module, *args, **kwargs)
......
......@@ -6,8 +6,11 @@
# it under the terms of the zlib license. See the COPYING file.
"""
To use these, just use core.add_timed_event(event)
where event is an instance of one of these classes
Timed events are the standard way to schedule events for later in poezio.
Once created, they must be added to the list of checked events with
:py:func:`Core.add_timed_event` (within poezio) or with
:py:func:`.PluginAPI.add_timed_event` (within a plugin).
"""
import logging
......@@ -18,13 +21,22 @@ import datetime
class TimedEvent(object):
"""
An event with a callback that is called when the specified time is passed
An event with a callback that is called when the specified time is passed.
Note that these events can NOT be used for very small delay or a very
precise date, since the check for events is done once per second, as
a maximum.
The callback and its arguments should be passed as the lasts arguments.
"""
def __init__(self, date, callback, *args):
"""
Create a new timed event.
:param datetime.datetime date: Time at which the callback must be run.
:param function callback: The handler that will be executed.
:param \*args: Optional arguments passed to the handler.
"""
self._callback = callback
self.args = args
self.repetive = False
......@@ -42,7 +54,11 @@ class TimedEvent(object):
def has_timed_out(self, current_date):
"""
returns True if the callback should be called
Check if the event has timed out.
:param datetime.datetime current_date: The current date.
:returns: True if the callback should be called
:rtype: bool
"""
if self.next_call_date < current_date:
return True
......@@ -51,22 +67,33 @@ class TimedEvent(object):
def change_date(self, date):
"""
Simply change the date of the event
Simply change the date of the event.
:param datetime.datetime date: Next date.
"""
self.next_call_date = date
def add_delay(self, delay):
"""
Add a delay (in seconds) to the date
Add a delay (in seconds) to the date.
:param int delay: The delay to add.
"""
self.next_call_date += datetime.timedelta(seconds=delay)
class DelayedEvent(TimedEvent):
"""
The date is calculated from now + a delay in seconds
Use it if you want an event to happen in, e.g. 6 seconds
A TimedEvent, but with the date calculated from now + a delay in seconds.
Use it if you want an event to happen in, e.g. 6 seconds.
"""
def __init__(self, delay, callback, *args):
"""
Create a new DelayedEvent.
:param int delay: The number of seconds.
:param function callback: The handler that will be executed.
:param \*args: Optional arguments passed to the handler.
"""
date = datetime.datetime.now() + datetime.timedelta(seconds=delay)
TimedEvent.__init__(self, date, callback, *args)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment