Commit ebf51a0a authored by Vincent A's avatar Vincent A

add a few docstrings

parent 60953228
......@@ -757,6 +757,11 @@ class LoginBrowser(PagesBrowser):
raise NotImplementedError()
def do_logout(self):
"""
Logout from website.
By default, simply clears the cookies.
"""
self.session.cookies.clear()
......@@ -799,7 +804,21 @@ class StatesMixin(object):
class APIBrowser(DomainBrowser):
"""
A browser for API websites.
"""
def open(self, *args, **kwargs):
"""
Do a JSON request.
The "Content-Type" header is always set to "application/json".
:param data: if specified, format as JSON and send as request body
:type data: :class:`dict`
:param headers: if specified, add these headers to the request
:type headers: :class:`dict`
"""
if 'data' in kwargs:
kwargs['data'] = json.dumps(kwargs['data'])
if 'headers' not in kwargs:
......@@ -809,4 +828,10 @@ class APIBrowser(DomainBrowser):
return super(APIBrowser, self).open(*args, **kwargs)
def request(self, *args, **kwargs):
"""
Do a JSON request and parse the response.
:returns: a dict containing the parsed JSON server response
:rtype: :class:`dict`
"""
return self.open(*args, **kwargs).json()
......@@ -67,6 +67,7 @@ class WeboobCookieJar(requests.cookies.RequestsCookieJar):
yield cookie
def copy(self):
"""Return an object copy of the cookie jar."""
new_cj = type(self)()
new_cj.update(self)
return new_cj
......@@ -135,6 +135,10 @@ class Android(Profile):
class IPhone(Profile):
"""
An iphone profile for mobile websites and some API websites
"""
def __init__(self, application):
self.application = application
......
......@@ -35,6 +35,13 @@ class BackendAlreadyExists(Exception):
class BackendsConfig(object):
"""
Config of backends.
A backend is an instance of a module with a config.
A module can thus have multiple instances.
"""
class WrongPermissions(Exception):
pass
......@@ -63,6 +70,13 @@ class BackendsConfig(object):
u'Weboob will not start as long as config file %s is readable by group or other users.' % confpath)
def iter_backends(self):
"""
Iterate on backends.
:returns: each tuple contains the backend name, module name and module options
:rtype: :class:`tuple`
"""
config = RawConfigParser()
config.read(self.confpath)
changed = False
......@@ -94,6 +108,14 @@ class BackendsConfig(object):
return name in config.sections()
def add_backend(self, backend_name, module_name, params, edit=False):
"""
Add a backend to config.
:param backend_name: name of the backend in config
:param module_name: name of the Python submodule to run
:param params: params to pass to the module
:type params: :class:`dict`
"""
if not backend_name:
raise ValueError(u'Please give a name to the configured backend.')
config = RawConfigParser()
......@@ -112,9 +134,17 @@ class BackendsConfig(object):
config.write(f)
def edit_backend(self, backend_name, module_name, params):
"""Edit a backend from config."""
return self.add_backend(backend_name, module_name, params, True)
def get_backend(self, backend_name):
"""
Get options of backend.
:returns: a tuple with the module name and the module options dict
:rtype: tuple
"""
config = RawConfigParser()
config.read(self.confpath)
if not config.has_section(backend_name):
......@@ -134,6 +164,8 @@ class BackendsConfig(object):
return module_name, items
def remove_backend(self, backend_name):
"""Remove a backend from config."""
config = RawConfigParser()
config.read(self.confpath)
if not config.remove_section(backend_name):
......
......@@ -65,6 +65,7 @@ class BackendsCall(object):
self.tasks.put(backend)
def store_result(self, backend, result):
"""Store the result when a backend task finished."""
if result is None:
return
......@@ -73,6 +74,11 @@ class BackendsCall(object):
self.responses.put(result)
def backend_process(self, function, args, kwargs):
"""
Internal method to run a method of a backend.
As this method may be blocking, it should be run on its own thread.
"""
backend = self.tasks.get()
with backend:
try:
......@@ -136,6 +142,7 @@ class BackendsCall(object):
return thread
def wait(self):
"""Wait until all tasks are finished."""
self.tasks.join()
if self.errors:
......
......@@ -87,6 +87,7 @@ class LoadedModule(object):
return self.klass.iter_caps()
def has_caps(self, *caps):
"""Return True if module implements at least one of the caps."""
for c in caps:
if (isinstance(c, basestring) and c in [cap.__name__ for cap in self.iter_caps()]) or \
(type(c) == type and issubclass(self.klass, c)):
......
......@@ -73,6 +73,7 @@ class ModuleInfo(object):
self.urls = items['urls']
def has_caps(self, caps):
"""Return True if module implements at least one of the caps."""
if not isinstance(caps, (list, tuple)):
caps = [caps]
for c in caps:
......
......@@ -34,19 +34,53 @@ __all__ = ['Scheduler']
class IScheduler(object):
"""Interface of a scheduler."""
def schedule(self, interval, function, *args):
"""
Schedule an event.
:param interval: delay before calling the function
:type interval: int
:param function: function to call
:type function: callabale
:param args: arguments to give to function
:returns: an event identificator
"""
raise NotImplementedError()
def repeat(self, interval, function, *args):
"""
Repeat a call to a function
:param interval: interval between two calls
:type interval: int
:param function: function to call
:type function: callable
:param args: arguments to give to function
:returns: an event identificator
"""
raise NotImplementedError()
def cancel(self, ev):
"""
Cancel an event
:param ev: the event identificator
"""
raise NotImplementedError()
def run(self):
"""
Run the scheduler loop
"""
raise NotImplementedError()
def want_stop(self):
"""
Plan to stop the scheduler.
"""
raise NotImplementedError()
......@@ -63,6 +97,8 @@ class RepeatedTimer(Timer):
class Scheduler(IScheduler):
"""Scheduler using Python's :mod:`threading`."""
def __init__(self):
self.logger = getLogger('scheduler')
self.mutex = RLock()
......
......@@ -49,6 +49,13 @@ class BasePasteModule(CapPaste):
def image_mime(data_base64, supported_formats=('gif', 'jpeg', 'png')):
"""
Return the MIME type of an image or None.
:param data_base64: data to detect, base64 encoded
:type data_base64: str
:param supported_formats: restrict list of formats to test
"""
try:
beginning = data_base64[:24].decode('base64')
except binascii.Error:
......
......@@ -23,17 +23,49 @@ class ConfigError(Exception):
class IConfig(object):
"""
Interface for config storage.
Config stores keys and values. Each key is a path of components, allowing
to group multiple options.
"""
def load(self, default={}):
"""
Load config.
:param default: default values for the config
:type default: dict[:class:`str`]
"""
raise NotImplementedError()
def save(self):
"""Save config."""
raise NotImplementedError()
def set(self, *args):
"""
Set a config value.
:param args: all args except the last arg are the path of the option key.
:type args: str or object
"""
raise NotImplementedError()
def delete(self, *args):
"""
Delete an option from config.
:param args: path to the option key.
:type args: str
"""
raise NotImplementedError()
def get(self, *args, **kwargs):
"""
Get the value of an option.
:param args: path of the option key.
:param default: if specified, default value when path is not found
"""
raise NotImplementedError()
......@@ -49,6 +49,11 @@ def get_backtrace(empty="Empty backtrace."):
def get_bytes_size(size, unit_name):
r"""Converts a unit and a number into a number of bytes.
>>> get_bytes_size(2, 'KB')
2048
"""
unit_data = {
'bytes': 1,
'KB': 1024,
......@@ -105,6 +110,7 @@ def guess_encoding(stdio):
def limit(iterator, lim):
"""Iterate on the lim first elements of iterator."""
count = 0
iterator = iter(iterator)
while count < lim:
......
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