aboutsummaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorAkos PASZTORY <ext-akos.pasztory@nokia.com>2009-05-14 14:31:13 +0300
committerGergely Risko <ext-risko.gergely@nokia.com>2009-05-26 15:17:19 +0300
commit9efba78ba665662fb73cf810c6b7e146a992fd16 (patch)
tree3ae2f2fb72ef283800b34a9bb423f1cbebe6fd2a /python
parent338e88b325d0dbf3df8bb271bc0d168c21ce1ee6 (diff)
Bringing the python stuff alive.
Moved from tools/flexiprovider to python/ContextKit, added into the build infrastructure and adapted scripts.
Diffstat (limited to 'python')
-rw-r--r--python/ContextKit/CTypesHelpers.py (renamed from python/CTypesHelpers.py)0
-rw-r--r--python/ContextKit/ContextProvider.py (renamed from python/ContextProvider.py)0
-rw-r--r--python/ContextKit/__init__.py0
-rw-r--r--python/ContextKit/flexiprovider.py209
-rw-r--r--python/Makefile.am11
-rw-r--r--python/README29
-rwxr-xr-xpython/complex.py22
-rwxr-xr-xpython/context-provide39
-rwxr-xr-xpython/context-rlwrap35
-rwxr-xr-xpython/simple.py12
10 files changed, 357 insertions, 0 deletions
diff --git a/python/CTypesHelpers.py b/python/ContextKit/CTypesHelpers.py
index bada4e0d..bada4e0d 100644
--- a/python/CTypesHelpers.py
+++ b/python/ContextKit/CTypesHelpers.py
diff --git a/python/ContextProvider.py b/python/ContextKit/ContextProvider.py
index 6e8f61fd..6e8f61fd 100644
--- a/python/ContextProvider.py
+++ b/python/ContextKit/ContextProvider.py
diff --git a/python/ContextKit/__init__.py b/python/ContextKit/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/python/ContextKit/__init__.py
diff --git a/python/ContextKit/flexiprovider.py b/python/ContextKit/flexiprovider.py
new file mode 100644
index 00000000..fd9a56ec
--- /dev/null
+++ b/python/ContextKit/flexiprovider.py
@@ -0,0 +1,209 @@
+#!/usr/bin/python
+import os
+import sys
+import tempfile
+import subprocess
+import glib, gobject
+import dbus, dbus.service, dbus.mainloop.glib
+
+def pkgconfig(*args):
+ """Runs `pkg-config $args` and returns the Popen object, augmented
+ with an `output' attribute containing stdout."""
+ cmd = ['pkg-config'] + list(args)
+ process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+ process.output = process.communicate()[0].strip()
+ return process
+
+# The following kludge is needed if we want this to be usable without
+# installing libcontextprovider. If pkg-config reports it as
+# uninstalled, we extend PYTHONPATH, then we try to import the module.
+# If that fails, extend LD_LIBRARY_PATH and re-exec ourselves.
+
+if pkgconfig('--exists', 'contextprovider-1.0').returncode != 0 or \
+ pkgconfig('--exists', 'duivaluespace-1.0').returncode != 0:
+ raise RuntimeError("You need to make pkg-config find "
+ "contextprovider-1.0 and duivaluespace-1.0 "
+ "somehow.\nTry setting $PKG_CONFIG_PATH.")
+
+if pkgconfig('--uninstalled', 'contextprovider-1.0').returncode == 0:
+ sys.path.append(pkgconfig('--variable=pythondir', 'contextprovider-1.0').output)
+try:
+ import ContextProvider as CP
+except ImportError:
+ raise
+except:
+ # Failed, probably because LD_LIBRARY_PATH is not right. Set it and
+ # re-exec ourselves. To avoid an infinite loop, we try this only
+ # when LD_LIBRARY_PATH doesn't yet contain what we want to add.
+ libdir = pkgconfig('--variable=libdir', 'contextprovider-1.0').output
+ ldpath = [d for d in os.environ.get('LD_LIBRARY_PATH', '').split(':') if d != '']
+ if libdir in ldpath:
+ raise
+ ldpath += [libdir, libdir + '/.libs']
+ env = dict(os.environ)
+ env.update(LD_LIBRARY_PATH=':'.join(ldpath))
+ os.execve(sys.argv[0], sys.argv, env)
+
+class _property(object):
+ """Kind-of a template for property types."""
+ def __init__(self, datatype, default):
+ self.datatype = datatype
+ self.default = default
+ self.name = None
+ def __call__(self, name, initial_value=None):
+ """Clones self, setting its name and initial value."""
+ if initial_value is None:
+ initial_value = self.default
+ prop = _property(self.datatype, initial_value)
+ prop.name = name
+ return prop
+
+# Pre-defined types and their defaults.
+INT = _property('INT', 0)
+STRING = _property('STRING', '')
+DOUBLE = _property('DOUBLE', 0.0)
+TRUTH = _property('TRUTH', False)
+
+def xmlfor(busname='ctx.flexiprovider', bus='session', *props):
+ """Generates a provider .xml document for the given properties
+ (which are instances of _property)."""
+ xml = ['<?xml version="1.0"?>\n'
+ '<provider bus="%s" service="%s">' % (bus, busname)]
+ for p in props:
+ node, _, key = p.name.partition('.')
+ xml.append(' <node name="%s">\n'
+ ' <key name="%s" type="%s">\n'
+ ' <doc>A phony but very flexible property.</doc>\n'
+ ' </key>\n'
+ ' </node>' % (node, key, p.datatype))
+ xml.append('</provider>')
+ return '\n'.join(xml)
+
+# Location of the update-context-providers script, initialized lazily
+# in the function below.
+U_C_P = None
+def update_context_providers(xml, dir='.'):
+ """Runs the update-context-provider script in $dir after writing
+ $xml to a temporary file."""
+ global U_C_P
+ if U_C_P is None:
+ U_C_P = ''
+ if pkgconfig('--uninstalled', 'duivaluespace-1.0').returncode == 0:
+ U_C_P = '%s/registry/' % pkgconfig('--variable=pcfiledir',
+ 'duivaluespace-1.0').output
+ U_C_P += 'update-context-providers'
+ tmpf = tempfile.NamedTemporaryFile('w+b', -1, '.xml', 'flexi', dir)
+ print >>tmpf, xml
+ tmpf.flush()
+ subprocess.call([U_C_P, dir])
+
+class Flexiprovider(object):
+ def stdin_ready(self, fd, cond):
+ if cond & (glib.IO_ERR | glib.IO_HUP):
+ self.loop.quit()
+ return False
+ # We assume that stdin is line-buffered (ie. readline() doesn't
+ # block (too much)). It's true if it's a tty. If piping from
+ # another program, do an entire line.
+ # TODO maybe we should rather fix ourselves
+ l = sys.stdin.readline().strip()
+ if l == '':
+ self.loop.quit()
+ return False
+ try:
+ # TODO something more sophisticated?
+ code = compile(l, '<input from stdin>', 'single')
+ eval(code, {}, dict(INT=INT,
+ STRING=STRING,
+ TRUTH=TRUTH,
+ DOUBLE=DOUBLE,
+ set=self.set,
+ get=self.get,
+ reset=self.reset,
+ quit=self.loop.quit,
+ add=self.add_and_update,
+ info=self.info))
+ except:
+ # We are ignoring errors.
+ import traceback
+ traceback.print_exc()
+ return True
+
+ def __init__(self, properties, name='ctx.flexiprovider', bus='session'):
+ self.busname = name
+ self.bus = bus
+ self.loop = gobject.MainLoop()
+ self.props = {}
+ self.values = {}
+ # Hook into libcontextprovider.
+ self.setters = dict(INT=CP.ContextProvider.set_integer,
+ STRING=CP.ContextProvider.set_string,
+ DOUBLE=CP.ContextProvider.set_double,
+ TRUTH=CP.ContextProvider.set_boolean)
+ self.subscribed_cb = CP.ContextProvider.SUBSCRIPTION_CHANGED_CALLBACK(lambda x, y: None)
+ CP.ContextProvider.init(dict(session=0, system=1)[self.bus], self.busname)
+ # Add properties and set the initial values.
+ for p in properties:
+ self.addproperty(p)
+ self.update_providers()
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ def get(self, prop):
+ return self.values.get(prop, None)
+
+ def set(self, prop, val):
+ if prop not in self.props:
+ return
+ self.values[prop] = val
+ if val is None:
+ CP.ContextProvider.set_null(prop)
+ else:
+ self.setters[self.props[prop].datatype](prop, val)
+
+ def reset(self, prop):
+ if prop not in self.props:
+ return
+ self.set(prop, self.props[prop].default)
+
+ def addproperty(self, prop):
+ self.props[prop.name] = prop
+ self.values[prop.name] = prop.default
+ CP.ContextProvider.install_key(prop.name, 0, self.subscribed_cb, None)
+ self.reset(prop.name)
+
+ def add_and_update(self, prop):
+ self.addproperty(prop)
+ self.update_providers()
+
+ def update_providers(self):
+ update_context_providers(xmlfor(self.busname, self.bus, *self.props.values()))
+
+ def info(self):
+ """Dumps information about currently provided properties."""
+ for p in self.props.values():
+ print p.datatype.ljust(8), p.name.ljust(20), \
+ self.values[p.name], '(', p.default, ')'
+
+ def interactive(self):
+ """Runs the provider interactively, accepting the following
+ commands from the standard input (note that this is Python
+ syntax, and it gets eval()ed):
+
+ set(property, value): sets the given property
+ get(property): prints the value of the property
+ reset(property): resets the property
+ add(propertydesc): adds a new property
+ info(): prints properties and their values
+ quit(): stops the whole thing
+
+ The provider will stop also if stdin is closed.
+ """
+ glib.io_add_watch(sys.stdin.fileno(),
+ glib.IO_IN | glib.IO_HUP | glib.IO_ERR,
+ self.stdin_ready)
+ self.loop.run()
+
+ def run(self):
+ self.loop.run()
+
+__all__ = ('INT', 'STRING', 'DOUBLE', 'TRUTH', 'Flexiprovider')
diff --git a/python/Makefile.am b/python/Makefile.am
new file mode 100644
index 00000000..c716a0b0
--- /dev/null
+++ b/python/Makefile.am
@@ -0,0 +1,11 @@
+dist_bin_SCRIPTS = context-provide \
+ context-rlwrap
+
+pkgpython_PYTHON = ContextKit/flexiprovider.py \
+ ContextKit/ContextProvider.py \
+ ContextKit/CTypesHelpers.py \
+ ContextKit/__init__.py
+
+EXTRA_DIST = README \
+ simple.py \
+ complex.py \ No newline at end of file
diff --git a/python/README b/python/README
new file mode 100644
index 00000000..64d0ddc9
--- /dev/null
+++ b/python/README
@@ -0,0 +1,29 @@
+These directories contain a simple Python binding for the
+libcontextprovider library and a test provider tool.
+
+Flexiprovider is a Python module/tool for testing Context Framework
+clients. It allows to define properties and provides means to change
+their values during runtime.
+
+There are two examples: `simple.py' and `complex.py', which you may use
+to get a clue how to write your own provider. The `context-provide'
+script allows you to start up a provider from the command line, without
+writing any Python code.
+
+The interactive mode supports the following commands on the standard
+input (note that this is Python syntax, and it gets eval()ed):
+
+ set(property, value): sets the given property
+ get(property): prints the value of the property
+ reset(property): resets the property
+ add(propertydesc): adds a new property, see simple.py for
+ examples of propertydesc
+ info(): prints properties and their values
+ quit(): stops the whole thing
+
+The provider will stop also if stdin is closed.
+
+You may use `context-rlwrap' as illustrated below to enjoy a more
+friendlier readline interface:
+
+ context-rlwrap ./provide.py my.fake.provider
diff --git a/python/complex.py b/python/complex.py
new file mode 100755
index 00000000..2adf166e
--- /dev/null
+++ b/python/complex.py
@@ -0,0 +1,22 @@
+#!/usr/bin/python
+"""A more complex provider with a horroristic theme."""
+
+from ContextKit.flexiprovider import *
+import glib
+
+count = 0
+def there_are_more():
+ global count
+ count += 1
+ fp.set('Zombie.Count', count)
+ if count == 2:
+ fp.add_and_update(STRING('You.Shout'))
+ fp.set('You.Shout', 'omg zombies!')
+ if count == 5:
+ fp.set('Life.isInDanger', True)
+ return True
+
+glib.timeout_add_seconds(2, there_are_more)
+fp = Flexiprovider([INT('Zombie.Count'), TRUTH('Life.isInDanger')],
+ 'omg.zombies', 'session')
+fp.run()
diff --git a/python/context-provide b/python/context-provide
new file mode 100755
index 00000000..4a3b5610
--- /dev/null
+++ b/python/context-provide
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+"""context-provide -- start up a provider from the command line
+
+Usage: context-provide [BUSTYPE:]PROVIDERNAME [TYPE NAME INITVALUE ...]
+
+Starts up a Flexiprovider with the given PROVIDERNAME, serving
+properties specified in the arguments. TYPE is one of 'int', 'string',
+'double', 'truth'. BUSTYPE is either 'system', 'session' (or void,
+defaulting to the latter).
+"""
+
+import sys
+from ContextKit.flexiprovider import *
+
+types = dict(int=(INT, int),
+ truth=(TRUTH, bool),
+ string=(STRING, str),
+ double=(DOUBLE, float))
+properties = []
+
+if len(sys.argv) < 2:
+ print __doc__
+ sys.exit(1)
+sys.argv.pop(0)
+busaddress = sys.argv.pop(0).split(':')
+if len(busaddress) == 1:
+ busaddress.insert(0, 'session')
+if busaddress[0] not in ('session', 'system'):
+ sys.exit(2)
+while len(sys.argv) >= 3:
+ datatype, name, initvalue = sys.argv[:3]
+ del sys.argv[:3]
+ if datatype not in types:
+ continue
+ cookiecutter, conversion = types[datatype]
+ properties.append(cookiecutter(name, conversion(initvalue)))
+
+provider = Flexiprovider(properties, busaddress[1], busaddress[0])
+provider.interactive()
diff --git a/python/context-rlwrap b/python/context-rlwrap
new file mode 100755
index 00000000..96702559
--- /dev/null
+++ b/python/context-rlwrap
@@ -0,0 +1,35 @@
+#!/usr/bin/python
+"""context-rlwrap -- a simple readline wrapper
+
+Usage: context-rlwrap [some other program and args]
+
+Wraps the plain stdin-based input mechanism of the given program into a
+convenient readline-based one.
+"""
+
+import os
+import sys
+import signal
+import readline
+
+if len(sys.argv) < 2:
+ sys.exit(__doc__)
+rfd, wfd = os.pipe()
+pid = os.fork()
+if pid == 0:
+ os.close(wfd)
+ os.dup2(rfd, 0)
+ sys.argv.pop(0)
+ os.execv(sys.argv[0], sys.argv)
+else:
+ def childied(n, f):
+ p, ec = os.waitpid(pid, 0)
+ sys.exit(ec)
+ signal.signal(signal.SIGCHLD, childied)
+ os.close(rfd)
+ try:
+ while True:
+ l = raw_input()
+ os.write(wfd, l + '\n')
+ except (KeyboardInterrupt, SystemExit): raise
+ except: pass
diff --git a/python/simple.py b/python/simple.py
new file mode 100755
index 00000000..a74cecdf
--- /dev/null
+++ b/python/simple.py
@@ -0,0 +1,12 @@
+#!/usr/bin/python
+"""
+A very simple example of the Flexiprovider. Exports three properties
+initially.
+"""
+
+from ContextKit.flexiprovider import *
+
+Flexiprovider([INT('location.altitude'),
+ STRING('my.name', 'flexi'),
+ TRUTH('is.out.there')],
+ 'ctx.flexi.provider', 'session').interactive()