diff --git a/weboob/browser/browsers.py b/weboob/browser/browsers.py index 09be4df11d65cc65b1c9d91e4e81723de2702733..9c3bc85f368abb2276a19ff499a540ddfd4b395f 100644 --- a/weboob/browser/browsers.py +++ b/weboob/browser/browsers.py @@ -757,6 +757,11 @@ def do_login(self): raise NotImplementedError() def do_logout(self): + """ + Logout from website. + + By default, simply clears the cookies. + """ self.session.cookies.clear() @@ -799,7 +804,21 @@ def dump_state(self): 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 @@ def open(self, *args, **kwargs): 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() diff --git a/weboob/browser/cookies.py b/weboob/browser/cookies.py index 8941c7887c9d4ff966f8a71c19968683d32dfb23..9e24db2842642bdf45258ef96e78259e265ea6e7 100644 --- a/weboob/browser/cookies.py +++ b/weboob/browser/cookies.py @@ -67,6 +67,7 @@ def make_cookies(self, response, request): yield cookie def copy(self): + """Return an object copy of the cookie jar.""" new_cj = type(self)() new_cj.update(self) return new_cj diff --git a/weboob/browser/profiles.py b/weboob/browser/profiles.py index c900f435e7c8dfc607823ad1ee903016ef37ebb8..c392c5592cac129923383133879d1c0f339b6cca 100644 --- a/weboob/browser/profiles.py +++ b/weboob/browser/profiles.py @@ -135,6 +135,10 @@ def setup_session(self, session): class IPhone(Profile): + """ + An iphone profile for mobile websites and some API websites + """ + def __init__(self, application): self.application = application diff --git a/weboob/core/backendscfg.py b/weboob/core/backendscfg.py index 66d54e8cea61343d1ec3c7203e1f74770e05916f..ef380e6152675ba9e134ecb6b020544d73a721a2 100644 --- a/weboob/core/backendscfg.py +++ b/weboob/core/backendscfg.py @@ -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 @@ def __init__(self, confpath): 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 @@ def backend_exists(self, name): 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 @@ def add_backend(self, backend_name, module_name, params, edit=False): 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 @@ def get_backend(self, backend_name): 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): diff --git a/weboob/core/bcall.py b/weboob/core/bcall.py index 4f00f839f0aeeb077b4e10e0f95c87cffc665e46..39678b5f5fbc760d07d03aa73ecef559497bb0d6 100644 --- a/weboob/core/bcall.py +++ b/weboob/core/bcall.py @@ -65,6 +65,7 @@ def __init__(self, backends, function, *args, **kwargs): 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 @@ def store_result(self, backend, result): 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 @@ def finishback() return thread def wait(self): + """Wait until all tasks are finished.""" self.tasks.join() if self.errors: diff --git a/weboob/core/modules.py b/weboob/core/modules.py index a57c987497a2994324554196703fd29efbdfd1f2..0b4bccfdbfca14679cc9ca94846dea0f4d9e73d2 100644 --- a/weboob/core/modules.py +++ b/weboob/core/modules.py @@ -87,6 +87,7 @@ def iter_caps(self): 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)): diff --git a/weboob/core/repositories.py b/weboob/core/repositories.py index 2fa4b1e9d6f877bfb2b0e8ab58f6760cf578319b..5ce1afd46a84710626d29d367d6ec46d635b179d 100644 --- a/weboob/core/repositories.py +++ b/weboob/core/repositories.py @@ -73,6 +73,7 @@ def load(self, items): 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: diff --git a/weboob/core/scheduler.py b/weboob/core/scheduler.py index 2b3a0378e5a6b23f7b64e0f649b11b732c826e28..1f49a25db0ab10ac93390162a9e03a80adbd5843 100644 --- a/weboob/core/scheduler.py +++ b/weboob/core/scheduler.py @@ -34,19 +34,53 @@ 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 @@ def run(self): class Scheduler(IScheduler): + """Scheduler using Python's :mod:`threading`.""" + def __init__(self): self.logger = getLogger('scheduler') self.mutex = RLock() diff --git a/weboob/tools/capabilities/paste.py b/weboob/tools/capabilities/paste.py index 54d26dd7ba62f91a22308f82d42fc2b8ee43ea52..175042a807963e808abfc30792c37cb576f14fd4 100644 --- a/weboob/tools/capabilities/paste.py +++ b/weboob/tools/capabilities/paste.py @@ -49,6 +49,13 @@ def get_closest_expiration(self, max_age): 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: diff --git a/weboob/tools/config/iconfig.py b/weboob/tools/config/iconfig.py index 46efd88d3786bc794f38a9704c448b9f84154325..b1d5782cc1f9639a0af77fd6985e787674138c5f 100644 --- a/weboob/tools/config/iconfig.py +++ b/weboob/tools/config/iconfig.py @@ -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() diff --git a/weboob/tools/misc.py b/weboob/tools/misc.py index 9bfd7f763c46e7e27c54a9768c33daa5a32f8ee5..96444a3629a484a6d051a23d7219bc63c4e1efdf 100644 --- a/weboob/tools/misc.py +++ b/weboob/tools/misc.py @@ -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: