simpleplugin

SimplePlugin micro-framework for Kodi content plugins

Author: Roman Miroshnychenko aka Roman V.M.

License: GPL v.3

Classes

Addon([id_]) Base addon class
Params(**kwargs) A class that stores parsed plugin call parameters
Plugin([id_]) Plugin class with URL query string routing.
RoutedPlugin([id_]) Plugin class that implements “pretty URL” routing similar to Flask and Bottle web-frameworks
Storage(storage_dir[, filename]) Persistent storage for arbitrary data with a dictionary-like interface
MemStorage(storage_id) In-memory storage with dict-like interface

Exceptions

SimplePluginError Custom exception
exception simpleplugin.SimplePluginError[source]

Bases: exceptions.Exception

Custom exception

simpleplugin.py2_encode(s, encoding=u'utf-8')[source]

Encode Python 2 unicode to str

In Python 3 the string is not changed.

simpleplugin.py2_decode(s, encoding=u'utf-8')[source]

Decode Python 2 str to unicode

In Python 3 the string is not changed.

simpleplugin.log_exception(*args, **kwds)[source]

Diagnostic helper context manager

It controls execution within its context and writes extended diagnostic info to the Kodi log if an unhandled exception happens within the context. The info includes the following items:

  • System info
  • Kodi version
  • Module path.
  • Code fragment where the exception has happened.
  • Global variables.
  • Local variables.

After logging the diagnostic info the exception is re-raised.

Example:

with log_exception():
    # Some risky code
    raise RuntimeError('Fatal error!')
Parameters:logger – logger function which must accept a single argument which is a log message. By default it is xbmc.log() with ERROR level.
class simpleplugin.Params(**kwargs)[source]

Bases: future.types.newdict.newdict

A class that stores parsed plugin call parameters

Parameters can be accessed both through dict keys and instance properties.

Note

For a missing parameter an instance property returns None.

Example:

@plugin.action('foo')
def action(params):
    foo = params['foo']  # Access by key
    bar = params.bar  # Access through property. Both variants are equal
class simpleplugin.Storage(storage_dir, filename='storage.pcl')[source]

Bases: _abcoll.MutableMapping

Persistent storage for arbitrary data with a dictionary-like interface

It is designed as a context manager and better be used with ‘with’ statement.

Parameters:
  • storage_dir (str) – directory for storage
  • filename (str) – the name of a storage file (optional)

Usage:

with Storage('/foo/bar/storage/') as storage:
    storage['key1'] = value1
    value2 = storage['key2']

Note

After exiting with block a Storage instance is invalidated. Storage contents are saved to disk only for a new storage or if the contents have been changed.

flush()[source]

Save storage contents to disk

This method saves new and changed Storage contents to disk and invalidates the Storage instance. Unchanged Storage is not saved but simply invalidated.

copy()[source]

Make a copy of storage contents

Note

this method performs a deep copy operation.

Returns:a copy of storage contents
Return type:dict
class simpleplugin.MemStorage(storage_id)[source]

Bases: _abcoll.MutableMapping

In-memory storage with dict-like interface

The data is stored in the Kodi core so contents of a MemStorage instance with the same ID can be shared between different Python processes.

Note

Keys are case-insensitive

Warning

MemStorage does not allow to modify mutable objects in place! You need to assign them to variables first, modify and store them back to a MemStorage instance.

Example:

storage = MemStorage('foo')
some_list = storage['bar']
some_list.append('spam')
storage['bar'] = some_list
Parameters:
  • storage_id (str) – ID of this storage instance
  • window_id (int) – the ID of a Kodi Window object where storage contents will be stored.
class simpleplugin.Addon(id_=u'')[source]

Bases: future.types.newobject.newobject

Base addon class

Provides access to basic addon parameters

Parameters:id (str) – addon id, e.g. ‘plugin.video.foo’ (optional)
addon

Kodi Addon instance that represents this Addon

Returns:Addon instance
Return type:xbmcaddon.Addon
id

Addon ID

Returns:Addon ID, e.g. ‘plugin.video.foo’
Return type:str
path

Addon path

Returns:path to the addon folder
Return type:unicode
icon

Addon icon

Returns:path to the addon icon image
Return type:str
fanart

Addon fanart

Returns:path to the addon fanart image
Return type:str
profile_dir

Addon config dir

Returns:path to the addon profile dir
Return type:str
version

Addon version

Returns:addon version
Return type:str
name

Addon name

Returns:addon name
Return type:str
author

Addon author

Returns:addon author
Return type:str
changelog

Addon changelog

Returns:addon changelog
Return type:str
description

Addon description

Returns:addon description
Return type:str
disclaimer

Addon disclaimer

Returns:addon disclaimer
Return type:str
stars

Addon stars

Returns:addon stars
Return type:str
summary

Addon summary

Returns:addon summary
Return type:str
type

Addon type

Returns:addon type
Return type:str
get_localized_string(id_)[source]

Get localized UI string

Parameters:id (int) – UI string ID
Returns:UI string in the current language
Return type:unicode
get_setting(id_, convert=True)[source]

Get addon setting

If convert=True, ‘bool’ settings are converted to Python bool values, and numeric strings to Python long or float depending on their format.

Note

Settings can also be read via Addon instance poperties named as the respective settings. I.e. addon.foo is equal to addon.get_setting('foo').

Parameters:
  • id (str) – setting ID
  • convert (bool) – try to guess and convert the setting to an appropriate type E.g. '1.0' will be converted to float 1.0 number, 'true' to True and so on.
Returns:

setting value

set_setting(id_, value)[source]

Set addon setting

Python bool type are converted to 'true' or 'false' Non-string/non-unicode values are converted to strings.

Warning

Setting values via Addon instance properties is not supported! Values can only be set using Addon.set_setting() method.

Parameters:
  • id (str) – setting ID
  • value – setting value
log(message, level=0)[source]

Add message to Kodi log starting with Addon ID

Parameters:
  • message (str) – message to be written into the Kodi log
  • level (int) – log level. xbmc module provides the necessary symbolic constants. Default: xbmc.LOGDEBUG
log_notice(message)[source]

Add NOTICE message to the Kodi log

Parameters:message (str) – message to write to the Kodi log
log_warning(message)[source]

Add WARNING message to the Kodi log

Parameters:message (str) – message to write to the Kodi log
log_error(message)[source]

Add ERROR message to the Kodi log

Parameters:message (str) – message to write to the Kodi log
log_debug(message)[source]

Add debug message to the Kodi log

Parameters:message (str) – message to write to the Kodi log
get_storage(filename=u'storage.pcl')[source]

Get a persistent Storage instance for storing arbitrary values between addon calls.

A Storage instance can be used as a context manager.

Example:

with plugin.get_storage() as storage:
    storage['param1'] = value1
    value2 = storage['param2']

Note

After exiting with block a Storage instance is invalidated.

Parameters:filename (str) – the name of a storage file (optional)
Returns:Storage object
Return type:Storage
get_mem_storage(storage_id=u'', window_id=10000)[source]

Creates an in-memory storage for this addon with dict-like interface

The storage can store picklable Python objects as long as Kodi is running and storage contents can be shared between Python processes. Different addons have separate storages, so storages with the same names created with this method do not conflict.

Example:

addon = Addon()
storage = addon.get_mem_storage()
foo = storage['foo']
storage['bar'] = bar
Parameters:
  • storage_id (str) – optional storage ID (case-insensitive).
  • window_id (int) – the ID of a Kodi Window object where storage contents will be stored.
Returns:

in-memory storage for this addon

Return type:

MemStorage

cached(duration=10)[source]

Cached decorator

Used to cache function return data

Usage:

@plugin.cached(30)
def my_func(*args, **kwargs):
    # Do some stuff
    return value
Parameters:duration (int) – caching duration in min (positive values only)
Raises:ValueError – if duration is zero or negative
mem_cached(duration=10)[source]

In-memory cache decorator

Usage:

@plugin.mem_cached(30)
def my_func(*args, **kwargs):
    # Do some stuff
    return value
Parameters:duration (int) – caching duration in min (positive values only)
Raises:ValueError – if duration is zero or negative
gettext(ui_string)[source]

Get a translated UI string from addon localization files.

This function emulates GNU Gettext for more convenient access to localized addon UI strings. To reduce typing this method object can be assigned to a _ (single underscore) variable.

For using gettext emulation Addon.initialize_gettext() method needs to be called first. See documentation for that method for more info about Gettext emulation.

Parameters:ui_string (str) – a UI string from English strings.po.
Returns:a UI string from translated strings.po.
Return type:unicode
Raises:SimplePluginError – if Addon.initialize_gettext() wasn’t called first or if a string is not found in English strings.po.
initialize_gettext()[source]

Initialize GNU gettext emulation in addon

Kodi localization system for addons is not very convenient because you need to operate with numeric string codes instead of UI strings themselves which reduces code readability and may lead to errors. The Addon class provides facilities for emulating GNU Gettext localization system.

This allows to use UI strings from addon’s English strings.po file instead of numeric codes to return localized strings from respective localized .po files.

This method returns Addon.gettext() method object that can be assigned to a short alias to reduce typing. Traditionally, _ (a single underscore) is used for this purpose.

Example:

addon = simpleplugin.Addon()
_ = addon.initialize_gettext()

xbmcgui.Dialog().notification(_('Warning!'), _('Something happened'))

In the preceding example the notification strings will be replaced with localized versions if these strings are translated.

Returns:Addon.gettext() method object
Raises:SimplePluginError – if the addon’s English strings.po file is missing
class simpleplugin.Plugin(id_=u'')[source]

Bases: simpleplugin.Addon

Plugin class with URL query string routing.

It provides a simplified plugin call routing mechanism using URL query strings. A URL query string must contain “action” parameter that defines which function will be invoked during this plugin call.

Parameters:id (str) – plugin’s id, e.g. ‘plugin.video.foo’ (optional)
params

Get plugin call parameters

Returns:plugin call parameters
Return type:Params
handle

Get plugin handle

Returns:plugin handle
Return type:int
static get_params(paramstring)[source]

Convert a URL-encoded paramstring to a Python dict

Parameters:paramstring (str) – URL-encoded paramstring
Returns:parsed paramstring
Return type:Params
get_url(plugin_url=u'', **kwargs)[source]

Construct a callable URL for a virtual directory item

If plugin_url is empty, a current plugin URL is used. kwargs are converted to a URL-encoded string of plugin call parameters To call a plugin action, ‘action’ parameter must be used, if ‘action’ parameter is missing, then the plugin root action is called If the action is not added to Plugin actions, PluginError will be raised.

Parameters:
  • plugin_url (str) – plugin URL with trailing / (optional)
  • kwargs – pairs of key=value items
Returns:

a full plugin callback URL

Return type:

str

action(name=None)[source]

Action decorator

Defines plugin callback action. If action’s name is not defined explicitly, then the action is named after the decorated function.

Warning

Action’s name must be unique.

A plugin must have at least one action named 'root' implicitly or explicitly.

Example:

# The action is implicitly named 'root' after the decorated function
@plugin.action()
def root(params):
    pass

@plugin.action('foo')  # The action name is set explicitly
def foo_action(params):
    pass
Parameters:name (str) – action’s name (optional).
Raises:SimplePluginError – if the action with such name is already defined.
run()[source]

Run plugin

Raises:SimplePluginError – if unknown action string is provided.
class simpleplugin.RoutedPlugin(id_=u'')[source]

Bases: simpleplugin.Plugin

Plugin class that implements “pretty URL” routing similar to Flask and Bottle web-frameworks

Parameters:id (str) – plugin’s id, e.g. ‘plugin.video.foo’ (optional)
url_for(func_, *args, **kwargs)[source]

Build a URL for a plugin route

This method performs reverse resolving a plugin callback URL for the named route. If route’s name is not set explicitly, then the name of a decorated function is used as the name of the corresponding route. The method can optionally take positional args and kwargs. If any positional args are provided their values replace variable placeholders by position.

Warning

The number of positional args must not exceed the number of variable placeholders!

If any kwargs are provided their values replace variable placeholders by name. If the number of kwargs provided exceeds the number of variable placeholders, then the rest of the kwargs are added to the URL as a query string.

Note

All unicode arguments are encoded with UTF-8 encoding.

Let’s assume that the ID of our plugin is plugin.acme. The following examples will show how to use this method to resolve callback URLs for this plugin.

Example 1:

@plugin.route('/foo')
def foo():
    pass
url = plugin.url_for('foo')
# url = 'plugin://plugin.acme/foo'

Example 2:

@plugin.route('/foo/<param>')
def foo(param):
    pass
url = plugin.url_for('foo', param='bar')
# url = 'plugin://plugin.acme/foo/bar'

Example 3:

plugin.route('/foo/<param>')
def foo(param):
    pass
url = plugin.url_for('foo', param='bar', ham='spam')
# url = 'plugin://plugin.acme/foo/bar?ham=spam
Parameters:
  • func (str or types.FunctionType) – route’s name or a decorated function object.
  • args – positional arguments.
  • kwargs – keyword arguments.
Returns:

full plugin call URL for the route.

Return type:

str

Raises:

simpleplugin.SimplePluginError – if a route with such name does not exist or on arguments mismatch.

get_url(func_, *args, **kwargs)

Build a URL for a plugin route

This method performs reverse resolving a plugin callback URL for the named route. If route’s name is not set explicitly, then the name of a decorated function is used as the name of the corresponding route. The method can optionally take positional args and kwargs. If any positional args are provided their values replace variable placeholders by position.

Warning

The number of positional args must not exceed the number of variable placeholders!

If any kwargs are provided their values replace variable placeholders by name. If the number of kwargs provided exceeds the number of variable placeholders, then the rest of the kwargs are added to the URL as a query string.

Note

All unicode arguments are encoded with UTF-8 encoding.

Let’s assume that the ID of our plugin is plugin.acme. The following examples will show how to use this method to resolve callback URLs for this plugin.

Example 1:

@plugin.route('/foo')
def foo():
    pass
url = plugin.url_for('foo')
# url = 'plugin://plugin.acme/foo'

Example 2:

@plugin.route('/foo/<param>')
def foo(param):
    pass
url = plugin.url_for('foo', param='bar')
# url = 'plugin://plugin.acme/foo/bar'

Example 3:

plugin.route('/foo/<param>')
def foo(param):
    pass
url = plugin.url_for('foo', param='bar', ham='spam')
# url = 'plugin://plugin.acme/foo/bar?ham=spam
Parameters:
  • func (str or types.FunctionType) – route’s name or a decorated function object.
  • args – positional arguments.
  • kwargs – keyword arguments.
Returns:

full plugin call URL for the route.

Return type:

str

Raises:

simpleplugin.SimplePluginError – if a route with such name does not exist or on arguments mismatch.

route(pattern, name=None)[source]

Route decorator for plugin callback routes

The route decorator is used to define plugin callback routes similar to a URL routing mechanism in Flask and Bottle Python web-frameworks. The plugin routing mechanism calls decorated functions by matching a path in a plugin callback URL (passed as sys.argv[0]) to a route pattern. A route pattern must start with a forward slash /. An end slash is optional. A plugin must have at least the root route with '/' pattern. Bu default a route is named by the decorated function, but route’s name can be set explicitly by providing the 2nd optional name argument.

Warning

Route names must be unique!

Example 1:

@plugin.route('/foo')
def foo_function():
    pass

In the preceding example foo_function will be called when the plugin is invoked with plugin://plugin.acme/foo/ callback URL. A route pattern can contain variable placeholders (marked with angular brackets <>) that are used to pass arguments to a route function.

Example 2:

@plugin.route('/foo/<param>')
def foo_function(param):
    pass

In the preceding example the part of a callback path marked with <param> placeholder will be passed to the function as an argument. The name of a placeholder must be the same as the name of the corresponding parameter. By default arguments are passed as strings. The int and float prefixes can be used to pass arguments as int and float numbers, for example <int:foo> or <float:bar>.

Example 3:

@plugin.route('/add/<int:param1>/<int:param2>')
def addition(param1, param2):
    sum = param1 + param2

A function can have multiple route decorators. In this case additional routes must have explicitly defined names. If a route has less variable placeholders than function parameters, “missing” function parameters must have default values.

Example 4:

@plugin.route('/foo/<param>', name='foo_route')
@plugin.route('/bar')
def some_function(param='spam'):
    # Do something

In the preceding example some_function can be called through 2 possible routes. If the function is called through the 1st route ('foo_route') <param> value will be passed as an argument. The 2nd route will call the function with the default argument 'spam' because this route has no variable placeholders to pass arguments to the function. The order of the route decorators does not matter but each route must have a unique name.

Note

A route pattern must start with a forward slash / and must not have a slash at the end.

Parameters:
  • pattern (str) – route matching pattern
  • name (str) – route’s name (optional). If no name is provided, the route is named after the decorated function. The name must be unique.