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
This commit is contained in:
mathieui 2013-03-08 19:39:34 +01:00
parent 0a2bd90c6d
commit dbde08a526
4 changed files with 151 additions and 61 deletions

View file

@ -6,7 +6,7 @@ Allows the creation and the removal of personal aliases.
from plugin import BasePlugin from plugin import BasePlugin
import common import common
from common import parse_command_args_to_alias as parse from common import shell_split
class Plugin(BasePlugin): class Plugin(BasePlugin):
def init(self): def init(self):
@ -27,18 +27,18 @@ class Plugin(BasePlugin):
""" """
arg = common.shell_split(line) arg = common.shell_split(line)
if len(arg) < 2: if len(arg) < 2:
self.core.information('Alias: Not enough parameters', 'Error') self.api.information('Alias: Not enough parameters', 'Error')
return return
alias = arg[0] alias = arg[0]
command = arg[1] command = arg[1]
tmp_args = arg[2] if len(arg) > 2 else '' tmp_args = arg[2] if len(arg) > 2 else ''
if alias in self.core.commands or alias in self.commands: 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 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.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): def command_unalias(self, alias):
""" """
@ -47,7 +47,7 @@ class Plugin(BasePlugin):
if alias in self.commands: if alias in self.commands:
del self.commands[alias] del self.commands[alias]
self.del_command(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): def completion_unalias(self, the_input):
aliases = [alias for alias in self.commands] aliases = [alias for alias in self.commands]
@ -61,6 +61,6 @@ class Plugin(BasePlugin):
pass pass
if name in self.core.commands: if name in self.core.commands:
return self.core.commands[name][0] return self.core.commands[name][0]
elif name in self.core.current_tab().commands: elif name in self.api.current_tab().commands:
return self.core.current_tab().commands[name][0] return self.api.current_tab().commands[name][0]
return dummy return dummy

View file

@ -6,7 +6,7 @@
# it under the terms of the zlib license. See the COPYING file. # it under the terms of the zlib license. See the COPYING file.
""" """
various useful functions Various useful functions.
""" """
from datetime import datetime, timedelta from datetime import datetime, timedelta
@ -29,6 +29,13 @@ ROOM_STATE_HL = 13
def get_base64_from_file(path): def get_base64_from_file(path):
""" """
Convert the content of a file to base64 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): if not os.path.isfile(path):
return (None, None, "File does not exist") return (None, None, "File does not exist")
@ -44,7 +51,11 @@ def get_base64_from_file(path):
def get_output_of_command(command): 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: try:
return subprocess.check_output(command.split()).decode('utf-8').split('\n') 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): def is_in_path(command, return_abs_path=False):
""" """
Return True if 'command' is found in one of the directories in the user's Check if *command* is in the $PATH or not.
path. If 'return_abs_path' is True, return the absolute path of the first
found command instead. Return False otherwise and on errors :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): for directory in os.getenv('PATH').split(os.pathsep):
try: try:
@ -95,6 +111,8 @@ def get_os_info():
""" """
Returns a detailed and well formated string containing Returns a detailed and well formated string containing
informations about the operating system informations about the operating system
:rtype: str
""" """
if os.name == 'posix': if os.name == 'posix':
executable = 'lsb_release' executable = 'lsb_release'
@ -151,14 +169,21 @@ def get_os_info():
def datetime_tuple(timestamp): 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: are handled:
- Optional milliseconds appened to the string are removed
- Optional Z (that means UTC) appened to the string are removed * Optional milliseconds appened to the string are removed
- XEP-082 datetime strings have all '-' cahrs removed to meet * Optional Z (that means UTC) appened to the string are removed
the above format. * 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.split('.')[0]
timestamp = timestamp.replace('-', '') timestamp = timestamp.replace('-', '')
@ -172,6 +197,17 @@ def datetime_tuple(timestamp):
return ret return ret
def shell_split(st): 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 = shlex.shlex(st, posix=True)
sh.commenters = '' sh.commenters = ''
sh.whitespace_split = True sh.whitespace_split = True
@ -188,7 +224,12 @@ def shell_split(st):
def parse_str_to_secs(duration=''): 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") >>> parse_str_to_secs("1d3m1h")
90180 90180
""" """
@ -210,8 +251,15 @@ def parse_str_to_secs(duration=''):
def parse_secs_to_str(duration=0): 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. Parse a number of seconds to a human-readable string.
The string has the form XdXhXmXs. 0 units are removed. 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) >>> parse_secs_to_str(3601)
1h1s 1h1s
""" """
@ -230,36 +278,13 @@ def parse_secs_to_str(duration=0):
result = '0s' result = '0s'
return result 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"
"""
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
def safeJID(*args, **kwargs): def safeJID(*args, **kwargs):
"""
Construct a :py:class:`sleekxmpp.JID` object from a string.
Used to avoid tracebacks during is stringprep fails
(fall back to a JID with an empty string).
"""
try: try:
return JID(*args, **kwargs) return JID(*args, **kwargs)
except InvalidJID: except InvalidJID:

View file

@ -6,6 +6,7 @@ These are used in the plugin system added in poezio 0.7.5
import os import os
from functools import partial from functools import partial
from configparser import RawConfigParser from configparser import RawConfigParser
from timed_events import TimedEvent, DelayedEvent
import config import config
import inspect import inspect
import traceback import traceback
@ -120,12 +121,49 @@ class PluginAPI(object):
def add_timed_event(self, _, *args, **kwargs): 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) 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): def information(self, _, *args, **kwargs):
""" """
Display a new message in the information buffer. Display a new message in the information buffer.
@ -139,7 +177,7 @@ class PluginAPI(object):
""" """
Get the current Tab. Get the current Tab.
:returns: tabs.Tab The current tab. :returns: The current tab.
""" """
return self.core.current_tab() return self.core.current_tab()
@ -176,7 +214,7 @@ class PluginAPI(object):
Example string: "<server> [port]" 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) return self.plugin_manager.add_command(module, *args, **kwargs)

View file

@ -6,8 +6,11 @@
# it under the terms of the zlib license. See the COPYING file. # it under the terms of the zlib license. See the COPYING file.
""" """
To use these, just use core.add_timed_event(event) Timed events are the standard way to schedule events for later in poezio.
where event is an instance of one of these classes
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 import logging
@ -18,13 +21,22 @@ import datetime
class TimedEvent(object): 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 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 precise date, since the check for events is done once per second, as
a maximum. a maximum.
The callback and its arguments should be passed as the lasts arguments. The callback and its arguments should be passed as the lasts arguments.
""" """
def __init__(self, date, callback, *args): 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._callback = callback
self.args = args self.args = args
self.repetive = False self.repetive = False
@ -42,7 +54,11 @@ class TimedEvent(object):
def has_timed_out(self, current_date): 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: if self.next_call_date < current_date:
return True return True
@ -51,22 +67,33 @@ class TimedEvent(object):
def change_date(self, date): 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 self.next_call_date = date
def add_delay(self, delay): 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) self.next_call_date += datetime.timedelta(seconds=delay)
class DelayedEvent(TimedEvent): class DelayedEvent(TimedEvent):
""" """
The date is calculated from now + a delay in 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 Use it if you want an event to happen in, e.g. 6 seconds.
""" """
def __init__(self, delay, callback, *args): 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) date = datetime.datetime.now() + datetime.timedelta(seconds=delay)
TimedEvent.__init__(self, date, callback, *args) TimedEvent.__init__(self, date, callback, *args)