pyliblo-0.10.0/0000775000175000017500000000000012513144230012577 5ustar dasdas00000000000000pyliblo-0.10.0/doc/0000775000175000017500000000000012513144230013344 5ustar dasdas00000000000000pyliblo-0.10.0/doc/theme/0000775000175000017500000000000012513144230014446 5ustar dasdas00000000000000pyliblo-0.10.0/doc/theme/pydoctheme/0000775000175000017500000000000012513144230016607 5ustar dasdas00000000000000pyliblo-0.10.0/doc/theme/pydoctheme/static/0000775000175000017500000000000012513144230020076 5ustar dasdas00000000000000pyliblo-0.10.0/doc/theme/pydoctheme/static/pydoctheme.css0000644000175000017500000000514512501131656022761 0ustar dasdas00000000000000@import url("default.css"); body { background-color: white; margin-left: 1em; margin-right: 1em; } div.related { margin-bottom: 1.2em; padding: 0.5em 0; border-top: 1px solid #ccc; margin-top: 0.5em; } div.related a:hover { color: #0095C4; } div.related:first-child { border-top: 0; border-bottom: 1px solid #ccc; } div.sphinxsidebar { background-color: #eeeeee; border-radius: 5px; line-height: 130%; font-size: smaller; } div.sphinxsidebar h3, div.sphinxsidebar h4 { margin-top: 1.5em; } div.sphinxsidebarwrapper > h3:first-child { margin-top: 0.2em; } div.sphinxsidebarwrapper > ul > li > ul > li { margin-bottom: 0.4em; } div.sphinxsidebar a:hover { color: #0095C4; } div.sphinxsidebar input { font-family: 'Lucida Grande',Arial,sans-serif; border: 1px solid #999999; font-size: smaller; border-radius: 3px; } div.sphinxsidebar input[type=text] { max-width: 150px; } div.body { padding: 0 0 0 1.2em; } div.body p { line-height: 140%; } div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6 { margin: 0; border: 0; padding: 0.3em 0; } div.body hr { border: 0; background-color: #ccc; height: 1px; } div.body pre { border-radius: 3px; border: 1px solid #ac9; } div.body div.admonition, div.body div.impl-detail { border-radius: 3px; } div.body div.impl-detail > p { margin: 0; } div.body div.seealso { border: 1px solid #dddd66; } div.body a { color: #00608f; } div.body a:visited { color: #30306f; } div.body a:hover { color: #00B0E4; } tt, pre { font-family: monospace, sans-serif; font-size: 96.5%; } div.body tt { border-radius: 3px; } div.body tt.descname { font-size: 120%; } div.body tt.xref, div.body a tt { font-weight: normal; } .deprecated { border-radius: 3px; } table.docutils { border: 1px solid #ddd; min-width: 20%; border-radius: 3px; margin-top: 10px; margin-bottom: 10px; } table.docutils td, table.docutils th { border: 1px solid #ddd !important; border-radius: 3px; } table p, table li { text-align: left !important; } table.docutils th { background-color: #eee; padding: 0.3em 0.5em; } table.docutils td { background-color: white; padding: 0.3em 0.5em; } table.footnote, table.footnote td { border: 0 !important; } div.footer { line-height: 150%; margin-top: -2em; text-align: right; width: auto; margin-right: 10px; } div.footer a:hover { color: #0095C4; } .refcount { color: #060; } .stableabi { color: #229; } pyliblo-0.10.0/doc/theme/pydoctheme/theme.conf0000644000175000017500000000102312501131656020557 0ustar dasdas00000000000000[theme] inherit = default stylesheet = pydoctheme.css pygments_style = sphinx [options] bodyfont = 'Lucida Grande', Arial, sans-serif headfont = 'Lucida Grande', Arial, sans-serif footerbgcolor = white footertextcolor = #555555 relbarbgcolor = white relbartextcolor = #666666 relbarlinkcolor = #444444 sidebarbgcolor = white sidebartextcolor = #444444 sidebarlinkcolor = #444444 bgcolor = white textcolor = #222222 linkcolor = #0090c0 visitedlinkcolor = #00608f headtextcolor = #1a1a1a headbgcolor = white headlinkcolor = #aaaaaa pyliblo-0.10.0/doc/theme/nasophon/0000775000175000017500000000000012513144230016273 5ustar dasdas00000000000000pyliblo-0.10.0/doc/theme/nasophon/static/0000775000175000017500000000000012513144230017562 5ustar dasdas00000000000000pyliblo-0.10.0/doc/theme/nasophon/static/nasophon.css0000644000175000017500000000744212501131656022133 0ustar dasdas00000000000000@import url("default.css"); body { background-color: white; margin-left: 1em; margin-right: 1em; width: 68em; margin: 0 auto; font-size: 95%; } div.related { margin-bottom: 1.2em; padding: 0.5em 0; border-top: 1px solid #ccc; margin-top: 0.5em; } div.related:first-child { border-top: 0; border-bottom: 1px solid #ccc; } div.sphinxsidebar { background-color: #f4f4f4; border-radius: 5px; line-height: 110%; } div.sphinxsidebar ul, div.sphinxsidebar ul ul { list-style: none; } div.sphinxsidebar li { margin-bottom: 0.5em; } div.sphinxsidebar li > ul { margin-top: 0.5em; } div.sphinxsidebar h3, div.sphinxsidebar h4 { margin-top: 1.5em; } div.sphinxsidebarwrapper > h3:first-child { margin-top: 0.2em; } div.sphinxsidebarwrapper { padding: 10px 10px 0px 10px; } div.sphinxsidebarwrapper > ul { margin-left: 0px; margin-right: 0px; } div.sphinxsidebar li.current { background: #fff; width: auto; border-radius: 3px; padding-bottom: 1px; } div.sphinxsidebar a.current { font-weight: bold; color: #fff; background: #52576a; border: 3px solid #52576a; border-radius: 3px; width: auto; display: block; } div.sphinxsidebar input { font-family: 'Lucida Grande',Arial,sans-serif; border: 1px solid #999999; font-size: smaller; border-radius: 3px; } div.sphinxsidebar input[type=text] { max-width: 150px; } div.body { padding: 0 0.6em 0 2.2em; } div.body p { line-height: 140%; } div.body h1 { margin: 0 -0.4em 0 -0.6em; border: 0; color: #ffffff; background: #52576a; border-radius: 5px; font-size: 150%; } div.body h1 a, div.body h1 a:visited { color: #ffffff; } div.body h2 { margin: 1.5em -0.45em 0 -0.65em; border: 0; color: #111111; background: #eeeeee; border-radius: 5px; font-size: 135%; } div.body h3 { font-weight: bold; color: #444; border: 0; font-size: 105%; padding-left: 1.25em; margin-right: 0em; padding-bottom: 0em; margin-bottom: 0em; } div.body hr { border: 0; background-color: #ccc; height: 1px; } div.body pre { border-radius: 3px; border: 1px solid #ac9; } div.body div.admonition, div.body div.impl-detail { border-radius: 3px; } div.body div.impl-detail > p { margin: 0; } div.body div.seealso { border: 1px solid #dddd66; } div.body div.note { background-color: #ffc; border: 1px solid #dd6; } div.body div.note tt { background: transparent; } div.body a { color: #127; } div.body a:visited { color: #127; } tt, pre { font-family: monospace, sans-serif; font-size: 93%; } div.body tt { border-radius: 3px; } div.body tt.descname { font-size: 100%; } div.body tt.xref, div.body a tt { } .deprecated { border-radius: 3px; } table.docutils { border: 1px solid #ddd; min-width: 20%; border-radius: 3px; margin-top: 10px; margin-bottom: 10px; } table.docutils td, table.docutils th { border: 1px solid #ddd !important; border-radius: 3px; } table p, table li { text-align: left !important; } table.docutils th { background-color: #eee; padding: 0.3em 0.5em; } table.docutils td { background-color: white; padding: 0.3em 0.5em; } table.footnote, table.footnote td { border: 0 !important; } div.footer { line-height: 150%; margin-top: -2em; text-align: right; width: auto; margin-right: 10px; } /* fix space below last line in multi-line table cells */ td .line-block { margin-bottom: 0; } /* No line break before first line of parameter description */ td.field-body strong+p { display: inline; } /* No blank lines within parameter lists */ dd, dl { margin-bottom: 0px; } a.headerlink { float: right; } pyliblo-0.10.0/doc/theme/nasophon/theme.conf0000644000175000017500000000076312501131656020255 0ustar dasdas00000000000000[theme] inherit = default stylesheet = nasophon.css pygments_style = sphinx [options] bodyfont = 'Lucida Grande', Arial, sans-serif headfont = 'Lucida Grande', Arial, sans-serif footerbgcolor = white footertextcolor = #555 relbarbgcolor = #fff relbartextcolor = #666 relbarlinkcolor = #444 sidebarbgcolor = white sidebartextcolor = #111 sidebarlinkcolor = #111 bgcolor = white textcolor = #222 linkcolor = #127 visitedlinkcolor = #0127 headtextcolor = #111 headbgcolor = white headlinkcolor = #aaa pyliblo-0.10.0/doc/Makefile0000644000175000017500000000036212501131656015010 0ustar dasdas00000000000000SPHINXOPTS = SPHINXBUILD = sphinx-build BUILDDIR = build ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(SPHINXOPTS) . .PHONY: clean html html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html clean: -rm -rf $(BUILDDIR)/* pyliblo-0.10.0/doc/index.rst0000644000175000017500000000573612501167470015226 0ustar dasdas00000000000000.. module:: liblo ############################## pyliblo 0.10 API Documentation ############################## Homepage: http://das.nasophon.de/pyliblo/ The latest version of this manual can be found at http://dsacre.github.io/pyliblo/doc/. For the most part, pyliblo is just a thin wrapper around `liblo `_, which does all the real work. For questions not answered here, also see the `liblo documentation `_ and the `OSC spec `_. Module-level Functions ====================== .. autofunction:: send .. autofunction:: time OSC Server Classes ================== .. autoclass:: Server :no-members: .. automethod:: __init__ .. automethod:: recv .. automethod:: send .. automethod:: add_method .. automethod:: del_method .. automethod:: register_methods .. automethod:: add_bundle_handlers .. autoattribute:: url .. autoattribute:: port .. autoattribute:: protocol .. automethod:: fileno .. automethod:: free ------- .. autoclass:: ServerThread :no-members: .. automethod:: __init__ .. automethod:: start .. automethod:: stop .. autoclass:: make_method .. automethod:: __init__ Utility Classes =============== .. autoclass:: Address :no-members: .. automethod:: __init__ .. autoattribute:: url .. autoattribute:: hostname .. autoattribute:: port .. autoattribute:: protocol ------- .. autoclass:: Message .. automethod:: __init__ .. autoclass:: Bundle .. automethod:: __init__ ------- .. autoexception:: ServerError .. autoexception:: AddressError Mapping between OSC and Python data types ========================================= When constructing a message, pyliblo automatically converts arguments to an appropriate OSC data type. To explicitly specify the OSC data type to be transmitted, pass a ``(typetag, data)`` tuple instead. Some types can't be unambiguously recognized, so they can only be sent that way. The mapping between OSC and Python data types is shown in the following table: ========= =============== ==================================================== typetag OSC data type Python data type ========= =============== ==================================================== ``'i'`` int32 :class:`int` ``'h'`` int64 :class:`long` (Python 2.x), :class:`int` (Python 3.x) ``'f'`` float :class:`float` ``'d'`` double :class:`float` ``'c'`` char :class:`str` (single character) ``'s'`` string :class:`str` ``'S'`` symbol :class:`str` ``'m'`` midi :class:`tuple` of four :class:`int`\ s ``'t'`` timetag :class:`float` ``'T'`` true ``'F'`` false ``'N'`` nil ``'I'`` infinitum ``'b'`` blob :class:`list` of :class:`int`\ s (Python 2.x), :class:`bytes` (Python 3.x) ========= =============== ==================================================== pyliblo-0.10.0/doc/conf.py0000644000175000017500000000446712501131656014661 0ustar dasdas00000000000000# -*- coding: utf-8 -*- # import sys, os sys.path.insert(0, os.path.abspath('..')) #extensions = ['sphinx.ext.autodoc', 'sphinxcontrib.fulltoc'] extensions = ['sphinx.ext.autodoc'] templates_path = ['templates'] html_theme_path = ['theme'] exclude_patterns = ['build'] source_suffix = '.rst' master_doc = 'index' project = u'pyliblo' copyright = u'2007-2014, Dominic Sacré' version = '0.10.0' release = '' html_theme = 'nasophon' html_copy_source = False pygments_style = 'sphinx' add_module_names = False autodoc_member_order = 'bysource' autodoc_default_flags = ['members', 'undoc-members'] from sphinx.ext.autodoc import py_ext_sig_re from sphinx.util.docstrings import prepare_docstring from sphinx.domains.python import PyClassmember, PyObject, py_sig_re def process_docstring(app, what, name, obj, options, lines): """ Remove leading function signatures from docstring. """ while len(lines) and py_ext_sig_re.match(lines[0]) is not None: del lines[0] def process_signature(app, what, name, obj, options, signature, return_annotation): """ Replace function signature with those specified in the docstring. """ if hasattr(obj, '__doc__') and obj.__doc__ is not None: lines = prepare_docstring(obj.__doc__) siglines = [] for line in lines: if py_ext_sig_re.match(line) is not None: siglines.append(line) else: break if len(siglines): siglines[0] = siglines[0][siglines[0].index('('):] return ('\n'.join(siglines), None) return (signature, return_annotation) # monkey-patch PyClassmember.handle_signature() to replace __init__ # with the class name. handle_signature_orig = PyClassmember.handle_signature def handle_signature(self, sig, signode): if '__init__' in sig: m = py_sig_re.match(sig) name_prefix, name, arglist, retann = m.groups() sig = sig.replace('__init__', name_prefix[:-1]) return handle_signature_orig(self, sig, signode) PyClassmember.handle_signature = handle_signature # prevent exception fields from collapsing PyObject.doc_field_types[2].can_collapse = False def setup(app): app.connect('autodoc-process-docstring', process_docstring) app.connect('autodoc-process-signature', process_signature) pyliblo-0.10.0/scripts/0000775000175000017500000000000012513144230014266 5ustar dasdas00000000000000pyliblo-0.10.0/scripts/send_osc.10000644000175000017500000000133312343704354016155 0ustar dasdas00000000000000.TH send_osc 1 .SH NAME send_osc \- Sends a single OSC message .SH SYNOPSIS .B send_osc \fIport\fP \fIpath\fP [,\fItypes\fP] [\fIargs\fP...] .SH DESCRIPTION .B send_osc sends an OSC message to the specified \fIport\fP (UDP port number, or any other address string supported by liblo). The message is delivered to \fIpath\fP. If the first argument following the path starts with a comma, it's interpreted as a type string, specifying the OSC data types to be used for sending the message arguments. Otherwise, send_osc automatically tries to use appropriate data types. Valid integer and float values are sent as such, anything else is sent as a string. .SH AUTHOR Dominic Sacre .SH SEE ALSO dump_osc(1) pyliblo-0.10.0/scripts/dump_osc.py0000755000175000017500000000412112406137264016462 0ustar dasdas00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # # pyliblo - Python bindings for the liblo OSC library # # Copyright (C) 2007-2011 Dominic Sacré # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # import sys import liblo class DumpOSC: def blob_to_hex(self, b): return " ".join([ (hex(v/16).upper()[-1] + hex(v%16).upper()[-1]) for v in b ]) def callback(self, path, args, types, src): write = sys.stdout.write ## print source #write("from " + src.get_url() + ": ") # print path write(path + " ,") # print typespec write(types) # loop through arguments and print them for a, t in zip(args, types): write(" ") if t == None: #unknown type write("[unknown type]") elif t == 'b': # it's a blob write("[" + self.blob_to_hex(a) + "]") else: # anything else write(str(a)) write('\n') def __init__(self, port = None): # create server object try: self.server = liblo.Server(port) except liblo.ServerError as err: sys.exit(str(err)) print("listening on URL: " + self.server.get_url()) # register callback function for all messages self.server.add_method(None, None, self.callback) def run(self): # just loop and dispatch messages every 10ms while True: self.server.recv(10) if __name__ == '__main__': # display help if len(sys.argv) == 1 or sys.argv[1] in ("-h", "--help"): sys.exit("Usage: " + sys.argv[0] + " port") # require one argument (port number) if len(sys.argv) < 2: sys.exit("please specify a port or URL") app = DumpOSC(sys.argv[1]) try: app.run() except KeyboardInterrupt: del app pyliblo-0.10.0/scripts/dump_osc.10000644000175000017500000000050112343704354016165 0ustar dasdas00000000000000.TH dump_osc 1 .SH NAME dump_osc \- Prints incoming OSC messages .SH SYNOPSIS .B dump_osc \fIport\fP .SH DESCRIPTION .B dump_osc prints all OSC messages received on \fIport\fP (UDP port number, or any other address string supported by liblo). .SH AUTHOR Dominic Sacre .SH SEE ALSO send_osc(1) pyliblo-0.10.0/scripts/send_osc.py0000755000175000017500000000334512406136626016456 0ustar dasdas00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # # pyliblo - Python bindings for the liblo OSC library # # Copyright (C) 2007-2011 Dominic Sacré # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # import sys import liblo def make_message_auto(path, *args): msg = liblo.Message(path) for a in args: try: v = int(a) except ValueError: try: v = float(a) except ValueError: v = a msg.add(v) return msg def make_message_manual(path, types, *args): if len(types) != len(args): sys.exit("length of type string doesn't match number of arguments") msg = liblo.Message(path) try: for a, t in zip(args, types): msg.add((t, a)) except Exception as e: sys.exit(str(e)) return msg if __name__ == '__main__': # display help if len(sys.argv) == 1 or sys.argv[1] in ("-h", "--help"): sys.exit("Usage: " + sys.argv[0] + " port path [,types] [args...]") # require at least two arguments (target port/url and message path) if len(sys.argv) < 2: sys.exit("please specify a port or URL") if len(sys.argv) < 3: sys.exit("please specify a message path") if len(sys.argv) > 3 and sys.argv[3].startswith(','): msg = make_message_manual(sys.argv[2], sys.argv[3][1:], *sys.argv[4:]) else: msg = make_message_auto(*sys.argv[2:]) try: liblo.send(sys.argv[1], msg) except IOError as e: sys.exit(str(e)) else: sys.exit(0) pyliblo-0.10.0/PKG-INFO0000664000175000017500000000040112513144230013667 0ustar dasdas00000000000000Metadata-Version: 1.0 Name: pyliblo Version: 0.10.0 Summary: Python bindings for the liblo OSC library Home-page: http://das.nasophon.de/pyliblo/ Author: Dominic Sacré Author-email: dominic.sacre@gmx.de License: LGPL Description: UNKNOWN Platform: UNKNOWN pyliblo-0.10.0/pyliblo.egg-info/0000775000175000017500000000000012513144230015743 5ustar dasdas00000000000000pyliblo-0.10.0/pyliblo.egg-info/PKG-INFO0000664000175000017500000000040112513144227017041 0ustar dasdas00000000000000Metadata-Version: 1.0 Name: pyliblo Version: 0.10.0 Summary: Python bindings for the liblo OSC library Home-page: http://das.nasophon.de/pyliblo/ Author: Dominic Sacré Author-email: dominic.sacre@gmx.de License: LGPL Description: UNKNOWN Platform: UNKNOWN pyliblo-0.10.0/pyliblo.egg-info/not-zip-safe0000664000175000017500000000000112513144227020177 0ustar dasdas00000000000000 pyliblo-0.10.0/pyliblo.egg-info/SOURCES.txt0000664000175000017500000000117412513144230017632 0ustar dasdas00000000000000COPYING MANIFEST.in NEWS README setup.py doc/Makefile doc/conf.py doc/index.rst doc/theme/nasophon/theme.conf doc/theme/nasophon/static/nasophon.css doc/theme/pydoctheme/theme.conf doc/theme/pydoctheme/static/pydoctheme.css examples/example_client.py examples/example_server.py examples/example_server_deco.py examples/test_server_thread.py pyliblo.egg-info/PKG-INFO pyliblo.egg-info/SOURCES.txt pyliblo.egg-info/dependency_links.txt pyliblo.egg-info/not-zip-safe pyliblo.egg-info/top_level.txt scripts/dump_osc.1 scripts/dump_osc.py scripts/send_osc.1 scripts/send_osc.py src/liblo.pxd src/liblo.pyx test/__init__.py test/test_liblo.pypyliblo-0.10.0/pyliblo.egg-info/top_level.txt0000664000175000017500000000000612513144227020477 0ustar dasdas00000000000000liblo pyliblo-0.10.0/pyliblo.egg-info/dependency_links.txt0000664000175000017500000000000112513144227022017 0ustar dasdas00000000000000 pyliblo-0.10.0/setup.cfg0000664000175000017500000000007312513144230014420 0ustar dasdas00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 pyliblo-0.10.0/setup.py0000775000175000017500000000334212513140145014317 0ustar dasdas00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import os import sys from distutils.command.build_scripts import build_scripts from distutils import util, log try: from setuptools import setup, Extension args = { 'test_suite': 'test', 'zip_safe': False, } except ImportError: from distutils.core import setup, Extension args = {} from Cython.Distutils import build_ext class build_scripts_rename(build_scripts): def copy_scripts(self): build_scripts.copy_scripts(self) # remove the .py extension from scripts for s in self.scripts: f = util.convert_path(s) before = os.path.join(self.build_dir, os.path.basename(f)) after = os.path.splitext(before)[0] log.info("renaming %s -> %s" % (before, after)) os.rename(before, after) cmdclass = { 'build_scripts': build_scripts_rename, 'build_ext': build_ext, } ext_modules = [ Extension( 'liblo', ['src/liblo.pyx'], extra_compile_args = [ '-fno-strict-aliasing', '-Werror-implicit-function-declaration', '-Wfatal-errors', ], libraries = ['lo'], ) ] setup( name = 'pyliblo', version = '0.10.0', author = 'Dominic Sacré', author_email = 'dominic.sacre@gmx.de', url = 'http://das.nasophon.de/pyliblo/', description = 'Python bindings for the liblo OSC library', license = 'LGPL', scripts = [ 'scripts/send_osc.py', 'scripts/dump_osc.py', ], data_files = [ ('share/man/man1', [ 'scripts/send_osc.1', 'scripts/dump_osc.1', ]), ], cmdclass = cmdclass, ext_modules = ext_modules, **args ) pyliblo-0.10.0/README0000644000175000017500000000117712501167470013473 0ustar dasdas00000000000000pyliblo - Python bindings for the liblo OSC library Copyright (C) 2007-2015 Dominic Sacré http://das.nasophon.de/pyliblo/ Requirements: ============= * Python >= 2.6 (also works with 3.x) [http://www.python.org/] * Cython [http://www.cython.org/] * liblo >= 0.27 [http://liblo.sourceforge.net/] Installation: ============= ./setup.py build ./setup.py install This will install both the python module and the send_osc/dump_osc scripts. Documentation: ============== See http://dsacre.github.io/pyliblo/doc/ for API documentation. The 'examples' directory in the source tree contains some example code. pyliblo-0.10.0/src/0000775000175000017500000000000012513144230013366 5ustar dasdas00000000000000pyliblo-0.10.0/src/liblo.pyx0000644000175000017500000007777712501173354015266 0ustar dasdas00000000000000# # pyliblo - Python bindings for the liblo OSC library # # Copyright (C) 2007-2015 Dominic Sacré # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2.1 of the # License, or (at your option) any later version. # __version__ = '0.10.0' from cpython cimport PY_VERSION_HEX cdef extern from 'Python.h': void PyEval_InitThreads() from libc.stdlib cimport malloc, free from libc.math cimport modf from libc.stdint cimport int32_t, int64_t from liblo cimport * import inspect as _inspect import weakref as _weakref class _weakref_method: """ Weak reference to a function, including support for bound methods. """ __slots__ = ('_func', 'obj') def __init__(self, f): if _inspect.ismethod(f): if PY_VERSION_HEX >= 0x03000000: self._func = f.__func__ self.obj = _weakref.ref(f.__self__) else: self._func = f.im_func self.obj = _weakref.ref(f.im_self) else: self._func = f self.obj = None @property def func(self): if self.obj: return self._func.__get__(self.obj(), self.obj().__class__) else: return self._func def __call__(self, *args, **kwargs): return self.func(*args, **kwargs) class struct: def __init__(self, **kwargs): for k, v in kwargs.items(): setattr(self, k, v) cdef str _decode(s): # convert to standard string type, depending on python version if PY_VERSION_HEX >= 0x03000000 and isinstance(s, bytes): return s.decode() else: return s cdef bytes _encode(s): # convert unicode to bytestring if isinstance(s, unicode): return s.encode() else: return s # forward declarations cdef class _ServerBase cdef class Address cdef class Message cdef class Bundle # liblo protocol constants UDP = LO_UDP TCP = LO_TCP UNIX = LO_UNIX ################################################################################ # timetag ################################################################################ cdef lo_timetag _double_to_timetag(double f): cdef lo_timetag tt cdef double intr, frac frac = modf(f, &intr) tt.sec = intr tt.frac = (frac * 4294967296.0) return tt cdef double _timetag_to_double(lo_timetag tt): return tt.sec + ((tt.frac) / 4294967296.0) def time(): """ Return the current time as a floating point number (seconds since January 1, 1900). """ cdef lo_timetag tt lo_timetag_now(&tt) return _timetag_to_double(tt) ################################################################################ # send ################################################################################ cdef _send(target, _ServerBase src, args): cdef lo_server from_server cdef Address target_address cdef int r # convert target to Address object, if necessary if isinstance(target, Address): target_address = target elif isinstance(target, tuple): # unpack tuple target_address = Address(*target) else: target_address = Address(target) # 'from' parameter is NULL if no server was specified from_server = src._server if src else NULL if isinstance(args[0], (Message, Bundle)): # args is already a list of Messages/Bundles packets = args else: # make a single Message from all arguments packets = [Message(*args)] # send all packets for p in packets: if isinstance(p, Message): message = p r = lo_send_message_from(target_address._address, from_server, message._path, message._message) else: bundle = p r = lo_send_bundle_from(target_address._address, from_server, bundle._bundle) if r == -1: raise IOError("sending failed: %s" % lo_address_errstr(target_address._address)) def send(target, *args): """ send(target, *messages) send(target, path, *args) Send messages to the the given target, without requiring a server. Arguments may be one or more :class:`Message` or :class:`Bundle` objects, or a single message given by its path and optional arguments. :param target: the address to send the message to; an :class:`Address` object, a port number, a ``(hostname, port)`` tuple, or a URL. :param messages: one or more objects of type :class:`Message` or :class:`Bundle`. :param path: the path of the message to be sent. :raises AddressError: if the given target is invalid. :raises IOError: if the message couldn't be sent. """ _send(target, None, args) ################################################################################ # Server ################################################################################ class ServerError(Exception): """ Raised when creating a liblo OSC server fails. """ def __init__(self, num, msg, where): self.num = num self.msg = msg self.where = where def __str__(self): s = "server error %d" % self.num if self.where: s += " in %s" % self.where s += ": %s" % self.msg return s cdef int _msg_callback(const_char *path, const_char *types, lo_arg **argv, int argc, lo_message msg, void *cb_data) with gil: cdef int i cdef char t cdef unsigned char *ptr cdef uint32_t size, j args = [] for i from 0 <= i < argc: t = types[i] if t == 'i': v = argv[i].i elif t == 'h': v = argv[i].h elif t == 'f': v = argv[i].f elif t == 'd': v = argv[i].d elif t == 'c': v = chr(argv[i].c) elif t == 's': v = _decode(&argv[i].s) elif t == 'S': v = _decode(&argv[i].s) elif t == 'T': v = True elif t == 'F': v = False elif t == 'N': v = None elif t == 'I': v = float('inf') elif t == 'm': v = (argv[i].m[0], argv[i].m[1], argv[i].m[2], argv[i].m[3]) elif t == 't': v = _timetag_to_double(argv[i].t) elif t == 'b': if PY_VERSION_HEX >= 0x03000000: v = bytes(lo_blob_dataptr(argv[i])) else: # convert binary data to python list v = [] ptr = lo_blob_dataptr(argv[i]) size = lo_blob_datasize(argv[i]) for j from 0 <= j < size: v.append(ptr[j]) else: v = None # unhandled data type args.append(v) cdef char *url = lo_address_get_url(lo_message_get_source(msg)) src = Address(url) free(url) cb = cb_data func = cb.func.func func_args = (_decode(path), args, _decode(types), src, cb.user_data) # call function if _inspect.getargspec(func)[1] == None: # determine number of arguments to call the function with n = len(_inspect.getargspec(func)[0]) if _inspect.ismethod(func): n -= 1 # self doesn't count r = cb.func(*func_args[0:n]) else: # function has argument list, pass all arguments r = cb.func(*func_args) return r if r != None else 0 cdef int _bundle_start_callback(lo_timetag t, void *cb_data) with gil: cb = cb_data r = cb.start_func(_timetag_to_double(t), cb.user_data) return r if r != None else 0 cdef int _bundle_end_callback(void *cb_data) with gil: cb = cb_data r = cb.end_func(cb.user_data) return r if r != None else 0 cdef void _err_handler(int num, const_char *msg, const_char *where) with gil: # can't raise exception in cdef callback function, so use a global variable # instead global __exception __exception = ServerError(num, msg, None) if where: __exception.where = where # decorator to register callbacks class make_method: """ A decorator that serves as a more convenient alternative to :meth:`Server.add_method()`. """ # counter to keep track of the order in which the callback functions where # defined _counter = 0 def __init__(self, path, types, user_data=None): """ make_method(path, typespec[, user_data]) Set the path and argument types for which the decorated method is to be registered. :param path: the message path to be handled by the registered method. ``None`` may be used as a wildcard to match any OSC message. :param typespec: the argument types to be handled by the registered method. ``None`` may be used as a wildcard to match any OSC message. :param user_data: An arbitrary object that will be passed on to the decorated method every time a matching message is received. """ self.spec = struct(counter=make_method._counter, path=path, types=types, user_data=user_data) make_method._counter += 1 def __call__(self, f): # we can't access the Server object here, because at the time the # decorator is run it doesn't even exist yet, so we store the # path/typespec in the function object instead... if not hasattr(f, '_method_spec'): f._method_spec = [] f._method_spec.append(self.spec) return f # common base class for both Server and ServerThread cdef class _ServerBase: cdef lo_server _server cdef list _keep_refs def __init__(self, **kwargs): self._keep_refs = [] if 'reg_methods' not in kwargs or kwargs['reg_methods']: self.register_methods() cdef _check(self): if self._server == NULL: raise RuntimeError("Server method called after free()") def register_methods(self, obj=None): """ register_methods(obj=None) Call :meth:`add_method()` for all methods of an object that are decorated with :func:`make_method`. :param obj: The object that implements the OSC callbacks to be registered. By default this is the server object itself. This function is usually called automatically by the server's constructor, unless its *reg_methods* parameter was set to ``False``. """ if obj == None: obj = self # find and register methods that were defined using decorators methods = [] for m in _inspect.getmembers(obj): if hasattr(m[1], '_method_spec'): for spec in m[1]._method_spec: methods.append(struct(spec=spec, name=m[1])) # sort by counter methods.sort(key=lambda x: x.spec.counter) for e in methods: self.add_method(e.spec.path, e.spec.types, e.name, e.spec.user_data) def get_url(self): self._check() cdef char *tmp = lo_server_get_url(self._server) cdef object r = tmp free(tmp) return _decode(r) def get_port(self): self._check() return lo_server_get_port(self._server) def get_protocol(self): self._check() return lo_server_get_protocol(self._server) def fileno(self): """ Return the file descriptor of the server socket, or -1 if not supported by the underlying server protocol. """ self._check() return lo_server_get_socket_fd(self._server) def add_method(self, path, typespec, func, user_data=None): """ add_method(path, typespec, func, user_data=None) Register a callback function for OSC messages with matching path and argument types. :param path: the message path to be handled by the registered method. ``None`` may be used as a wildcard to match any OSC message. :param typespec: the argument types to be handled by the registered method. ``None`` may be used as a wildcard to match any OSC message. :param func: the callback function. This may be a global function, a class method, or any other callable object, pyliblo will know what to do either way. :param user_data: An arbitrary object that will be passed on to *func* every time a matching message is received. """ cdef char *p cdef char *t if isinstance(path, (bytes, unicode)): s = _encode(path) p = s elif path == None: p = NULL else: raise TypeError("path must be a string or None") if isinstance(typespec, (bytes, unicode)): s2 = _encode(typespec) t = s2 elif typespec == None: t = NULL else: raise TypeError("typespec must be a string or None") self._check() # use a weak reference if func is a method, to avoid circular # references in cases where func is a method of an object that also # has a reference to the server (e.g. when deriving from the Server # class) cb = struct(func=_weakref_method(func), user_data=user_data) # keep a reference to the callback data around self._keep_refs.append(cb) lo_server_add_method(self._server, p, t, _msg_callback, cb) def del_method(self, path, typespec): """ del_method(path, typespec) Delete a callback function. For both *path* and *typespec*, ``None`` may be used as a wildcard. .. versionadded:: 0.9.2 """ cdef char *p cdef char *t if isinstance(path, (bytes, unicode)): s = _encode(path) p = s elif path == None: p = NULL else: raise TypeError("path must be a string or None") if isinstance(typespec, (bytes, unicode)): s2 = _encode(typespec) t = s2 elif typespec == None: t = NULL else: raise TypeError("typespec must be a string or None") self._check() lo_server_del_method(self._server, p, t) def add_bundle_handlers(self, start_handler, end_handler, user_data=None): """ add_bundle_handlers(start_handler, end_handler, user_data=None) Add bundle notification handlers. :param start_handler: a callback which fires when at the start of a bundle. This is called with the bundle's timestamp and user_data. :param end_handler: a callback which fires when at the end of a bundle. This is called with user_data. :param user_data: data to pass to the handlers. .. versionadded:: 0.10.0 """ cb_data = struct(start_func=_weakref_method(start_handler), end_func=_weakref_method(end_handler), user_data=user_data) self._keep_refs.append(cb_data) lo_server_add_bundle_handlers(self._server, _bundle_start_callback, _bundle_end_callback, cb_data) def send(self, target, *args): """ send(target, *messages) send(target, path, *args) Send a message or bundle from this server to the the given target. Arguments may be one or more :class:`Message` or :class:`Bundle` objects, or a single message given by its path and optional arguments. :param target: the address to send the message to; an :class:`Address` object, a port number, a ``(hostname, port)`` tuple, or a URL. :param messages: one or more objects of type :class:`Message` or :class:`Bundle`. :param path: the path of the message to be sent. :raises AddressError: if the given target is invalid. :raises IOError: if the message couldn't be sent. """ self._check() _send(target, self, args) property url: """ The server's URL. """ def __get__(self): return self.get_url() property port: """ The server's port number. """ def __get__(self): return self.get_port() property protocol: """ The server's protocol (one of the constants :const:`UDP`, :const:`TCP`, or :const:`UNIX`). """ def __get__(self): return self.get_protocol() cdef class Server(_ServerBase): """ A server that can receive OSC messages using a simple single-threaded polling model. Use :class:`ServerThread` for an OSC server that runs in its own thread and never blocks. """ def __init__(self, port=None, proto=LO_DEFAULT, **kwargs): """ Server(port[, proto]) Create a new :class:`!Server` object. :param port: a decimal port number or a UNIX socket path. If omitted, an arbitrary free UDP port will be used. :param proto: one of the constants :const:`UDP`, :const:`TCP`, or :const:`UNIX`; default is :const:`UDP`. :keyword reg_methods: ``False`` if you don't want the init function to automatically register callbacks defined with the :func:`make_method` decorator (keyword argument only). Exceptions: ServerError """ cdef char *cs if port != None: p = _encode(str(port)); cs = p else: cs = NULL global __exception __exception = None self._server = lo_server_new_with_proto(cs, proto, _err_handler) if __exception: raise __exception _ServerBase.__init__(self, **kwargs) def __dealloc__(self): self.free() def free(self): """ Free the underlying server object and close its port. Note that this will also happen automatically when the server is deallocated. """ if self._server: lo_server_free(self._server) self._server = NULL def recv(self, timeout=None): """ recv(timeout=None) Receive and dispatch one OSC message. Blocking by default, unless *timeout* is specified. :param timeout: Time in milliseconds after which the function returns if no messages have been received. *timeout* may be 0, in which case the function always returns immediately, whether messages have been received or not. :return: ``True`` if a message was received, otherwise ``False``. """ cdef int t, r self._check() if timeout != None: t = timeout with nogil: r = lo_server_recv_noblock(self._server, t) return r and True or False else: with nogil: lo_server_recv(self._server) return True cdef class ServerThread(_ServerBase): """ Unlike :class:`Server`, :class:`!ServerThread` uses its own thread which runs in the background to dispatch messages. :class:`!ServerThread` has the same methods as :class:`!Server`, with the exception of :meth:`Server.recv`. Instead, it defines two additional methods :meth:`start` and :meth:`stop`. .. note:: Because liblo creates its own thread to receive and dispatch messages, callback functions will not be run in the main Python thread! """ cdef lo_server_thread _server_thread def __init__(self, port=None, proto=LO_DEFAULT, **kwargs): """ ServerThread(port[, proto]) Create a new :class:`!ServerThread` object, which can receive OSC messages. Unlike :class:`Server`, :class:`ServerThread` uses its own thread which runs in the background to dispatch messages. Note that callback methods will not be run in the main Python thread! :param port: a decimal port number or a UNIX socket path. If omitted, an arbitrary free UDP port will be used. :param proto: one of the constants :const:`UDP`, :const:`TCP`, or :const:`UNIX`; default is :const:`UDP`. :keyword reg_methods: ``False`` if you don't want the init function to automatically register callbacks defined with the make_method decorator (keyword argument only). :raises ServerError: if creating the server fails, e.g. because the given port could not be opened. """ cdef char *cs if port != None: p = _encode(str(port)); cs = p else: cs = NULL # make sure python can handle threading PyEval_InitThreads() global __exception __exception = None self._server_thread = lo_server_thread_new_with_proto(cs, proto, _err_handler) if __exception: raise __exception self._server = lo_server_thread_get_server(self._server_thread) _ServerBase.__init__(self, **kwargs) def __dealloc__(self): self.free() def free(self): """ Free the underlying server object and close its port. Note that this will also happen automatically when the server is deallocated. """ if self._server_thread: lo_server_thread_free(self._server_thread) self._server_thread = NULL self._server = NULL def start(self): """ Start the server thread. liblo will now start to dispatch any messages it receives. """ self._check() lo_server_thread_start(self._server_thread) def stop(self): """ Stop the server thread. """ self._check() lo_server_thread_stop(self._server_thread) ################################################################################ # Address ################################################################################ class AddressError(Exception): """ Raised when trying to create an invalid :class:`Address` object. """ def __init__(self, msg): self.msg = msg def __str__(self): return "address error: %s" % self.msg cdef class Address: cdef lo_address _address def __init__(self, addr, addr2=None, proto=LO_UDP): """ Address(hostname, port[, proto]) Address(port) Address(url) Create a new :class:`!Address` object from the given hostname/port or URL. :param hostname: the target's hostname. :param port: the port number on the target. :param proto: one of the constants :const:`UDP`, :const:`TCP`, or :const:`UNIX`. :param url: a URL in liblo notation, e.g. ``'osc.udp://hostname:1234/'``. :raises AddressError: if the given parameters do not represent a valid address. """ if addr2: # Address(host, port[, proto]) s = _encode(addr) s2 = _encode(str(addr2)) self._address = lo_address_new_with_proto(proto, s, s2) if not self._address: raise AddressError("invalid protocol") elif isinstance(addr, int) or (isinstance(addr, str) and addr.isdigit()): # Address(port) s = str(addr).encode() self._address = lo_address_new(NULL, s) else: # Address(url) s = _encode(addr) self._address = lo_address_new_from_url(s) # lo_address_errno() is of no use if self._addr == NULL if not self._address: raise AddressError("invalid URL '%s'" % str(addr)) def __dealloc__(self): lo_address_free(self._address) def get_url(self): cdef char *tmp = lo_address_get_url(self._address) cdef object r = tmp free(tmp) return _decode(r) def get_hostname(self): return _decode(lo_address_get_hostname(self._address)) def get_port(self): cdef bytes s = lo_address_get_port(self._address) if s.isdigit(): return int(s) else: return _decode(s) def get_protocol(self): return lo_address_get_protocol(self._address) property url: """ The address's URL. """ def __get__(self): return self.get_url() property hostname: """ The address's hostname. """ def __get__(self): return self.get_hostname() property port: """ The address's port number. """ def __get__(self): return self.get_port() property protocol: """ The address's protocol (one of the constants :const:`UDP`, :const:`TCP`, or :const:`UNIX`). """ def __get__(self): return self.get_protocol() ################################################################################ # Message ################################################################################ cdef class _Blob: cdef lo_blob _blob def __init__(self, arr): # arr can by any sequence type cdef unsigned char *p cdef uint32_t size, i size = len(arr) if size < 1: raise ValueError("blob is empty") # copy each element of arr to a C array p = malloc(size) try: if isinstance(arr[0], (str, unicode)): # use ord() if arr is a string (but not bytes) for i from 0 <= i < size: p[i] = ord(arr[i]) else: for i from 0 <= i < size: p[i] = arr[i] # build blob self._blob = lo_blob_new(size, p) finally: free(p) def __dealloc__(self): lo_blob_free(self._blob) cdef class Message: """ An OSC message, consisting of a path and arbitrary arguments. """ cdef bytes _path cdef lo_message _message cdef list _keep_refs def __init__(self, path, *args): """ Message(path, *args) Create a new :class:`!Message` object. """ self._keep_refs = [] # encode path to bytestring if necessary self._path = _encode(path) self._message = lo_message_new() self.add(*args) def __dealloc__(self): lo_message_free(self._message) def add(self, *args): """ add(*args) Append the given arguments to the message. Arguments can be single values or ``(typetag, data)`` tuples. """ for arg in args: if (isinstance(arg, tuple) and len(arg) <= 2 and isinstance(arg[0], (bytes, unicode)) and len(arg[0]) == 1): # type explicitly specified if len(arg) == 2: self._add(arg[0], arg[1]) else: self._add(arg[0], None) else: # detect type automatically self._add_auto(arg) cdef _add(self, type, value): cdef uint8_t midi[4] # accept both bytes and unicode as type specifier cdef char t = ord(_decode(type)[0]) if t == 'i': lo_message_add_int32(self._message, int(value)) elif t == 'h': lo_message_add_int64(self._message, long(value)) elif t == 'f': lo_message_add_float(self._message, float(value)) elif t == 'd': lo_message_add_double(self._message, float(value)) elif t == 'c': lo_message_add_char(self._message, ord(value)) elif t == 's': s = _encode(value) lo_message_add_string(self._message, s) elif t == 'S': s = _encode(value) lo_message_add_symbol(self._message, s) elif t == 'T': lo_message_add_true(self._message) elif t == 'F': lo_message_add_false(self._message) elif t == 'N': lo_message_add_nil(self._message) elif t == 'I': lo_message_add_infinitum(self._message) elif t == 'm': for n from 0 <= n < 4: midi[n] = value[n] lo_message_add_midi(self._message, midi) elif t == 't': lo_message_add_timetag(self._message, _double_to_timetag(value)) elif t == 'b': b = _Blob(value) # make sure the blob is not deleted as long as this message exists self._keep_refs.append(b) lo_message_add_blob(self._message, (<_Blob>b)._blob) else: raise TypeError("unknown OSC data type '%c'" % t) cdef _add_auto(self, value): # bool is a subclass of int, so check those first if value is True: lo_message_add_true(self._message) elif value is False: lo_message_add_false(self._message) elif isinstance(value, (int, long)): try: lo_message_add_int32(self._message, value) except OverflowError: lo_message_add_int64(self._message, value) elif isinstance(value, float): lo_message_add_float(self._message, float(value)) elif isinstance(value, (bytes, unicode)): s = _encode(value) lo_message_add_string(self._message, s) elif value == None: lo_message_add_nil(self._message) elif value == float('inf'): lo_message_add_infinitum(self._message) else: # last chance: could be a blob try: iter(value) except TypeError: raise TypeError("unsupported message argument type") self._add('b', value) ################################################################################ # Bundle ################################################################################ cdef class Bundle: """ A bundle of one or more messages to be sent and dispatched together. """ cdef lo_bundle _bundle cdef list _keep_refs def __init__(self, *messages): """ Bundle([timetag, ]*messages) Create a new :class:`Bundle` object. You can optionally specify a time at which the messages should be dispatched (as an OSC timetag float), and any number of messages to be included in the bundle. """ cdef lo_timetag tt tt.sec, tt.frac = 0, 0 self._keep_refs = [] if len(messages) and not isinstance(messages[0], Message): t = messages[0] if isinstance(t, (float, int, long)): tt = _double_to_timetag(t) elif isinstance(t, tuple) and len(t) == 2: tt.sec, tt.frac = t else: raise TypeError("invalid timetag") # first argument was timetag, so continue with second messages = messages[1:] self._bundle = lo_bundle_new(tt) if len(messages): self.add(*messages) def __dealloc__(self): lo_bundle_free(self._bundle) def add(self, *args): """ add(*messages) add(path, *args) Add one or more messages to the bundle. """ if isinstance(args[0], Message): # args is already a list of Messages messages = args else: # make a single Message from all arguments messages = [Message(*args)] # add all messages for m in messages: self._keep_refs.append(m) message = m lo_bundle_add_message(self._bundle, message._path, message._message) pyliblo-0.10.0/src/liblo.pxd0000644000175000017500000001042412501167470015213 0ustar dasdas00000000000000# # pyliblo - Python bindings for the liblo OSC library # # Copyright (C) 2007-2011 Dominic Sacré # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2.1 of the # License, or (at your option) any later version. # from libc.stdint cimport int32_t, uint32_t, int64_t, uint8_t from libc.stdio cimport const_char cdef extern from 'lo/lo.h': # type definitions ctypedef void *lo_server ctypedef void *lo_server_thread ctypedef void *lo_method ctypedef void *lo_address ctypedef void *lo_message ctypedef void *lo_blob ctypedef void *lo_bundle ctypedef struct lo_timetag: uint32_t sec uint32_t frac ctypedef union lo_arg: int32_t i int64_t h float f double d unsigned char c char s uint8_t m[4] lo_timetag t cdef enum: LO_DEFAULT LO_UDP LO_UNIX LO_TCP ctypedef void(*lo_err_handler)(int num, const_char *msg, const_char *where) ctypedef int(*lo_method_handler)(const_char *path, const_char *types, lo_arg **argv, int argc, lo_message msg, void *user_data) ctypedef int(*lo_bundle_start_handler)(lo_timetag time, void *user_data) ctypedef int(*lo_bundle_end_handler)(void *user_data) # send int lo_send_message_from(lo_address targ, lo_server serv, char *path, lo_message msg) int lo_send_bundle_from(lo_address targ, lo_server serv, lo_bundle b) # server lo_server lo_server_new_with_proto(char *port, int proto, lo_err_handler err_h) void lo_server_free(lo_server s) char *lo_server_get_url(lo_server s) int lo_server_get_port(lo_server s) int lo_server_get_protocol(lo_server s) lo_method lo_server_add_method(lo_server s, char *path, char *typespec, lo_method_handler h, void *user_data) void lo_server_del_method(lo_server s, char *path, char *typespec) int lo_server_add_bundle_handlers(lo_server s, lo_bundle_start_handler sh, lo_bundle_end_handler eh, void *user_data) int lo_server_recv(lo_server s) nogil int lo_server_recv_noblock(lo_server s, int timeout) nogil int lo_server_get_socket_fd(lo_server s) # server thread lo_server_thread lo_server_thread_new_with_proto(char *port, int proto, lo_err_handler err_h) void lo_server_thread_free(lo_server_thread st) lo_server lo_server_thread_get_server(lo_server_thread st) void lo_server_thread_start(lo_server_thread st) void lo_server_thread_stop(lo_server_thread st) # address lo_address lo_address_new(char *host, char *port) lo_address lo_address_new_with_proto(int proto, char *host, char *port) lo_address lo_address_new_from_url(char *url) void lo_address_free(lo_address) char *lo_address_get_url(lo_address a) char *lo_address_get_hostname(lo_address a) char *lo_address_get_port(lo_address a) int lo_address_get_protocol(lo_address a) const_char* lo_address_errstr(lo_address a) # message lo_message lo_message_new() void lo_message_free(lo_message) void lo_message_add_int32(lo_message m, int32_t a) void lo_message_add_int64(lo_message m, int64_t a) void lo_message_add_float(lo_message m, float a) void lo_message_add_double(lo_message m, double a) void lo_message_add_char(lo_message m, char a) void lo_message_add_string(lo_message m, char *a) void lo_message_add_symbol(lo_message m, char *a) void lo_message_add_true(lo_message m) void lo_message_add_false(lo_message m) void lo_message_add_nil(lo_message m) void lo_message_add_infinitum(lo_message m) void lo_message_add_midi(lo_message m, uint8_t a[4]) void lo_message_add_timetag(lo_message m, lo_timetag a) void lo_message_add_blob(lo_message m, lo_blob a) lo_address lo_message_get_source(lo_message m) # blob lo_blob lo_blob_new(int32_t size, void *data) void lo_blob_free(lo_blob b) void *lo_blob_dataptr(lo_blob b) uint32_t lo_blob_datasize(lo_blob b) # bundle lo_bundle lo_bundle_new(lo_timetag tt) void lo_bundle_free(lo_bundle b) void lo_bundle_add_message(lo_bundle b, char *path, lo_message m) # timetag void lo_timetag_now(lo_timetag *t) pyliblo-0.10.0/test/0000775000175000017500000000000012513144230013556 5ustar dasdas00000000000000pyliblo-0.10.0/test/__init__.py0000644000175000017500000000000012501167470015663 0ustar dasdas00000000000000pyliblo-0.10.0/test/test_liblo.py0000755000175000017500000002374112501167470016310 0ustar dasdas00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # # pyliblo - Python bindings for the liblo OSC library # # Copyright (C) 2007-2015 Dominic Sacré # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # import unittest import re import time import sys import liblo def matchHost(host, regex): r = re.compile(regex) return r.match(host) != None class Arguments: def __init__(self, path, args, types, src, data): self.path = path self.args = args self.types = types self.src = src self.data = data class ServerTestCaseBase(unittest.TestCase): def setUp(self): self.cb = None def callback(self, path, args, types, src, data): self.cb = Arguments(path, args, types, src, data) def callback_dict(self, path, args, types, src, data): if self.cb == None: self.cb = { } self.cb[path] = Arguments(path, args, types, src, data) class ServerTestCase(ServerTestCaseBase): def setUp(self): ServerTestCaseBase.setUp(self) self.server = liblo.Server('1234') def tearDown(self): del self.server def testPort(self): self.assertEqual(self.server.port, 1234) def testURL(self): self.assertTrue(matchHost(self.server.url, 'osc\.udp://.*:1234/')) def testSendInt(self): self.server.add_method('/foo', 'i', self.callback, "data") self.server.send('1234', '/foo', 123) self.assertTrue(self.server.recv()) self.assertEqual(self.cb.path, '/foo') self.assertEqual(self.cb.args[0], 123) self.assertEqual(self.cb.types, 'i') self.assertEqual(self.cb.data, "data") self.assertTrue(matchHost(self.cb.src.url, 'osc\.udp://.*:1234/')) def testSendBlob(self): self.server.add_method('/blob', 'b', self.callback) self.server.send('1234', '/blob', [4, 8, 15, 16, 23, 42]) self.assertTrue(self.server.recv()) if sys.hexversion < 0x03000000: self.assertEqual(self.cb.args[0], [4, 8, 15, 16, 23, 42]) else: self.assertEqual(self.cb.args[0], b'\x04\x08\x0f\x10\x17\x2a') def testSendVarious(self): self.server.add_method('/blah', 'ihfdscb', self.callback) if sys.hexversion < 0x03000000: self.server.send(1234, '/blah', 123, 2**42, 123.456, 666.666, "hello", ('c', 'x'), (12, 34, 56)) else: self.server.send(1234, '/blah', 123, ('h', 2**42), 123.456, 666.666, "hello", ('c', 'x'), (12, 34, 56)) self.assertTrue(self.server.recv()) self.assertEqual(self.cb.types, 'ihfdscb') self.assertEqual(len(self.cb.args), len(self.cb.types)) self.assertEqual(self.cb.args[0], 123) self.assertEqual(self.cb.args[1], 2**42) self.assertAlmostEqual(self.cb.args[2], 123.456, 3) self.assertAlmostEqual(self.cb.args[3], 666.666, 3) self.assertEqual(self.cb.args[4], "hello") self.assertEqual(self.cb.args[5], 'x') if sys.hexversion < 0x03000000: self.assertEqual(self.cb.args[6], [12, 34, 56]) else: self.assertEqual(self.cb.args[6], b'\x0c\x22\x38') def testSendOthers(self): self.server.add_method('/blubb', 'tmSTFNI', self.callback) self.server.send(1234, '/blubb', ('t', 666666.666), ('m', (1, 2, 3, 4)), ('S', 'foo'), True, ('F',), None, ('I',)) self.assertTrue(self.server.recv()) self.assertEqual(self.cb.types, 'tmSTFNI') self.assertAlmostEqual(self.cb.args[0], 666666.666) self.assertEqual(self.cb.args[1], (1, 2, 3, 4)) self.assertEqual(self.cb.args[2], 'foo') self.assertEqual(self.cb.args[3], True) self.assertEqual(self.cb.args[4], False) self.assertEqual(self.cb.args[5], None) self.assertEqual(self.cb.args[6], float('inf')) def testSendMessage(self): self.server.add_method('/blah', 'is', self.callback) m = liblo.Message('/blah', 42, 'foo') self.server.send(1234, m) self.assertTrue(self.server.recv()) self.assertEqual(self.cb.types, 'is') self.assertEqual(self.cb.args[0], 42) self.assertEqual(self.cb.args[1], 'foo') def testSendLong(self): l = 1234567890123456 self.server.add_method('/long', 'h', self.callback) m = liblo.Message('/long', l) self.server.send(1234, m) self.assertTrue(self.server.recv()) self.assertEqual(self.cb.types, 'h') self.assertEqual(self.cb.args[0], l) def testSendingLongAsIntOverflows(self): l = 1234567890123456 with self.assertRaises(OverflowError): liblo.Message('/long', (l, 'i')) def testSendBundle(self): self.server.add_method('/foo', 'i', self.callback_dict) self.server.add_method('/bar', 's', self.callback_dict) self.server.send(1234, liblo.Bundle( liblo.Message('/foo', 123), liblo.Message('/bar', "blubb") )) self.assertTrue(self.server.recv(100)) self.assertEqual(self.cb['/foo'].args[0], 123) self.assertEqual(self.cb['/bar'].args[0], "blubb") def testSendTimestamped(self): self.server.add_method('/blubb', 'i', self.callback) d = 1.23 t1 = time.time() b = liblo.Bundle(liblo.time() + d) b.add('/blubb', 42) self.server.send(1234, b) while not self.cb: self.server.recv(1) t2 = time.time() self.assertAlmostEqual(t2 - t1, d, 1) def testSendInvalid(self): with self.assertRaises(TypeError): self.server.send(1234, '/blubb', ('x', 'y')) def testRecvTimeout(self): t1 = time.time() self.assertFalse(self.server.recv(500)) t2 = time.time() self.assertLess(t2 - t1, 0.666) def testRecvImmediate(self): t1 = time.time() self.assertFalse(self.server.recv(0)) t2 = time.time() self.assertLess(t2 - t1, 0.01) def testMethodAfterFree(self): self.server.free() with self.assertRaises(RuntimeError): self.server.recv() def testBundleCallbacksFire(self): def bundle_start_cb(timestamp, user_data): self.assertIsInstance(timestamp, float) user_data.append('start') def bundle_end_cb(user_data): user_data.append('end') bundle_data = [] self.server.add_bundle_handlers(bundle_start_cb, bundle_end_cb, bundle_data) self.testSendBundle() self.assertEqual(bundle_data, ['start', 'end']) class ServerCreationTestCase(unittest.TestCase): def testNoPermission(self): with self.assertRaises(liblo.ServerError): s = liblo.Server('22') def testRandomPort(self): s = liblo.Server() self.assertGreaterEqual(s.port, 1024) self.assertLessEqual(s.port, 65535) def testPort(self): s = liblo.Server(1234) t = liblo.Server('5678') self.assertEqual(s.port, 1234) self.assertEqual(t.port, 5678) self.assertTrue(matchHost(s.url, 'osc\.udp://.*:1234/')) def testPortProto(self): s = liblo.Server(1234, liblo.TCP) self.assertTrue(matchHost(s.url, 'osc\.tcp://.*:1234/')) class ServerTCPTestCase(ServerTestCaseBase): def setUp(self): ServerTestCaseBase.setUp(self) self.server = liblo.Server('1234', liblo.TCP) def tearDown(self): del self.server def testSendReceive(self): self.server.add_method('/foo', 'i', self.callback) liblo.send(self.server.url, '/foo', 123) self.assertTrue(self.server.recv()) self.assertEqual(self.cb.path, '/foo') self.assertEqual(self.cb.args[0], 123) self.assertEqual(self.cb.types, 'i') # def testNotReachable(self): # with self.assertRaises(IOError): # self.server.send('osc.tcp://192.168.23.42:4711', '/foo', 23, 42) class ServerThreadTestCase(ServerTestCaseBase): def setUp(self): ServerTestCaseBase.setUp(self) self.server = liblo.ServerThread('1234') def tearDown(self): del self.server def testSendAndReceive(self): self.server.add_method('/foo', 'i', self.callback) self.server.send('1234', '/foo', 42) self.server.start() time.sleep(0.2) self.server.stop() self.assertEqual(self.cb.args[0], 42) class DecoratorTestCase(unittest.TestCase): class TestServer(liblo.Server): def __init__(self): liblo.Server.__init__(self, 1234) @liblo.make_method('/foo', 'ibm') def foo_cb(self, path, args, types, src, data): self.cb = Arguments(path, args, types, src, data) def setUp(self): self.server = self.TestServer() def tearDown(self): del self.server def testSendReceive(self): liblo.send(1234, '/foo', 42, ('b', [4, 8, 15, 16, 23, 42]), ('m', (6, 6, 6, 0))) self.assertTrue(self.server.recv()) self.assertEqual(self.server.cb.path, '/foo') self.assertEqual(len(self.server.cb.args), 3) class AddressTestCase(unittest.TestCase): def testPort(self): a = liblo.Address(1234) b = liblo.Address('5678') self.assertEqual(a.port, 1234) self.assertEqual(b.port, 5678) self.assertEqual(a.url, 'osc.udp://localhost:1234/') def testUrl(self): a = liblo.Address('osc.udp://foo:1234/') self.assertEqual(a.url, 'osc.udp://foo:1234/') self.assertEqual(a.hostname, 'foo') self.assertEqual(a.port, 1234) self.assertEqual(a.protocol, liblo.UDP) def testHostPort(self): a = liblo.Address('foo', 1234) self.assertEqual(a.url, 'osc.udp://foo:1234/') def testHostPortProto(self): a = liblo.Address('foo', 1234, liblo.TCP) self.assertEqual(a.url, 'osc.tcp://foo:1234/') if __name__ == "__main__": unittest.main() pyliblo-0.10.0/examples/0000775000175000017500000000000012513144230014415 5ustar dasdas00000000000000pyliblo-0.10.0/examples/test_server_thread.py0000755000175000017500000000214412501167470020675 0ustar dasdas00000000000000#!/usr/bin/env python from __future__ import print_function import liblo import time st = liblo.ServerThread() print("Created Server Thread on Port", st.port) def foo_cb(path, args, types): print("foo_cb():") for a, t in zip(args, types): print("received argument %s of type %s" % (a, t)) def bar_cb(path, args, types, src): print("bar_cb():") print("message from", src.url) print("typespec:", types) for a, t in zip(args, types): print("received argument %s of type %s" % (a, t)) class Blah: def __init__(self, x): self.x = x def baz_cb(self, path, args, types, src, user_data): print("baz_cb():") print(args[0]) print("self.x is", self.x, ", user data was", user_data) st.add_method('/foo', 'ifs', foo_cb) st.add_method('/bar', 'hdc', bar_cb) b = Blah(123) st.add_method('/baz', 'b', b.baz_cb, 456) st.start() liblo.send(st.port, "/foo", 123, 456.789, "buh!") l = 1234567890123456 liblo.send(st.port, "/bar", l, 666, ('c', "x")) time.sleep(1) st.stop() st.start() liblo.send(st.port, "/baz", [1,2,3,4,5,6,7,8,9,10]) time.sleep(1) pyliblo-0.10.0/examples/example_server.py0000755000175000017500000000212512501167470020021 0ustar dasdas00000000000000#!/usr/bin/env python from __future__ import print_function import liblo, sys # create server, listening on port 1234 try: server = liblo.Server(1234) except liblo.ServerError as err: print(err) sys.exit() def foo_bar_callback(path, args): i, f = args print("received message '%s' with arguments '%d' and '%f'" % (path, i, f)) def foo_baz_callback(path, args, types, src, data): print("received message '%s'" % path) print("blob contains %d bytes, user data was '%s'" % (len(args[0]), data)) def fallback(path, args, types, src): print("got unknown message '%s' from '%s'" % (path, src.url)) for a, t in zip(args, types): print("argument of type '%s': %s" % (t, a)) # register method taking an int and a float server.add_method("/foo/bar", 'if', foo_bar_callback) # register method taking a blob, and passing user data to the callback server.add_method("/foo/baz", 'b', foo_baz_callback, "blah") # register a fallback for unhandled messages server.add_method(None, None, fallback) # loop and dispatch messages every 100ms while True: server.recv(100) pyliblo-0.10.0/examples/example_client.py0000755000175000017500000000162312501167470017773 0ustar dasdas00000000000000#!/usr/bin/env python from __future__ import print_function import liblo, sys # send all messages to port 1234 on the local machine try: target = liblo.Address(1234) except liblo.AddressError as err: print(err) sys.exit() # send message "/foo/message1" with int, float and string arguments liblo.send(target, "/foo/message1", 123, 456.789, "test") # send double, int64 and char liblo.send(target, "/foo/message2", ('d', 3.1415), ('h', 2**42), ('c', 'x')) # we can also build a message object first... msg = liblo.Message("/foo/blah") # ... append arguments later... msg.add(123, "foo") # ... and then send it liblo.send(target, msg) # send a list of bytes as a blob blob = [4, 8, 15, 16, 23, 42] liblo.send(target, "/foo/blob", blob) # wrap a message in a bundle, to be dispatched after 2 seconds bundle = liblo.Bundle(liblo.time() + 2.0, liblo.Message("/blubb", 123)) liblo.send(target, bundle) pyliblo-0.10.0/examples/example_server_deco.py0000755000175000017500000000132112501167470021010 0ustar dasdas00000000000000#!/usr/bin/env python from __future__ import print_function from liblo import * import sys # raw_input renamed to input in python3 try: input = raw_input except NameError: pass class MyServer(ServerThread): def __init__(self): ServerThread.__init__(self, 1234) @make_method('/foo', 'ifs') def foo_callback(self, path, args): i, f, s = args print("received message '%s' with arguments: %d, %f, %s" % (path, i, f, s)) @make_method(None, None) def fallback(self, path, args): print("received unknown message '%s'" % path) try: server = MyServer() except ServerError as err: print(err) sys.exit() server.start() input("press enter to quit...\n") pyliblo-0.10.0/MANIFEST.in0000644000175000017500000000043112501167470014341 0ustar dasdas00000000000000include README include COPYING include NEWS include setup.py include src/liblo.pyx include src/liblo.pxd include scripts/dump_osc.py include scripts/send_osc.py include scripts/dump_osc.1 include scripts/send_osc.1 include test/*.py include examples/*.py graft doc prune doc/build pyliblo-0.10.0/NEWS0000664000175000017500000001015412513140145013300 0ustar dasdas000000000000002015-04-14: pyliblo 0.10.0 * New and improved documentation, built from docstrings using Sphinx. * Add Server.add_bundle_handlers(), thanks to David Honour and Paul Weaver. * Fix sending 64-bit values with Python 3 and on some 64-bit systems, thanks to Sebastian Ramacher. * Cython is now a mandatory build dependency. The generated liblo.c has been removed from the release tarball. * The minimum version of liblo required by pyliblo is now 0.27. 2014-09-16: pyliblo 0.9.2 * Add function Server.del_method(), thanks to a patch from Артём Попов. * Use Python 3 compatible syntax in the send_osc and dump_osc scripts. This also means that 2.6 is now the minimum supported Python version. * Use setuptools if available, thanks to Chris Arndt. 2011-01-29: pyliblo 0.9.1 * Changed send functions to raise an IOError if sending failed (probably most useful with TCP connections). * Lots of code cleanup and deobfuscation. * Included unit tests in release tarball. 2010-10-22: pyliblo 0.9.0 * Support Python 3.x. As a result, pyliblo can no longer be built with Pyrex, and requires Cython >= 0.12 instead. * Added free() method to Server and ServerThread classes. * Added fileno() method to Server and ServerThread classes (thanks to Edward George). * Prefer read-only properties over getter methods (which still exist, but are now deprecated). * Added proper docstrings (copied from the HTML docs). * The minimum version of liblo required by pyliblo is now 0.26. 2009-11-30: pyliblo 0.8.1 * Release the Python GIL inside Server.recv(). * Fixed a possible segfault when the error handler was called from the * liblo server thread. 2009-09-13: pyliblo 0.8.0 * Changed license from GPL 2 to LGPL 2.1 (as did liblo in version 0.26). * Added protocol parameter to the Server class. Among other things, this allows TCP connections. * The minumum version of liblo required by pyliblo is now 0.24. * pyliblo can now be built with either Pyrex or Cython. 2009-01-19: pyliblo 0.7.2 * Fixed all compiler warnings properly in Pyrex, without patching the generated C code. * Return values of callback functions are no longer ignored, but handled as in liblo. * The send_osc script can now be run with an explicit type string, instead of trying to determine the argument types automatically. 2008-08-03: pyliblo 0.7.1 * Added manpages for send_osc and dump_osc. 2008-03-03: pyliblo 0.7.0 * Fixed memory leaks, caused by failure to free() the result of lo_server_get_url() and lo_address_get_url(). * Added parameter to Server.register_methods() to allow registering functions of an object other than the server itself. * Allow callback functions to have a variable number of arguments (*args). 2007-12-14: pyliblo 0.6.4 * Avoid creating circular references when using methods as callback functions, which in some cases prevented the server object from being deleted properly. 2007-08-10: pyliblo 0.6.3 * Patched the Pyrex-generated code to make it compile without warnings. * Always build from the existing C source by default. 2007-07-29: pyliblo 0.6.2 * Minor code cleanup, hopefully not breaking anything. * Somewhat faster conversion of blob data from and to Python lists. 2007-07-07: pyliblo 0.6.1 * Fixed a bug that caused the floats 0.0 and 1.0 to be sent as boolean. Thanks to Jesse Chappell for the patch. 2007-05-20: pyliblo 0.6 * Added support for sending bundles, optionally with timestamps. * Added previously unsupported OSC data types (timetag, midi, symbol, true/false/nil/infinitum). * New @make_method decorator. * Various bugfixes. 2007-04-28: pyliblo 0.5.1 * Fixed a stupid typo in Server.send(). 2007-04-26: pyliblo 0.5 * Simplified the way arguments are passed to callback functions. For the server side, this release is therefore incompatible with previous versions! * Some more cleanup. 2007-04-02: pyliblo 0.3 * Added class ServerThread for asynchronous dispatching of incoming messages. 2007-04-01: pyliblo 0.2 * Minor improvements. 2007-02-20: pyliblo 0.1 * Initial release. pyliblo-0.10.0/COPYING0000644000175000017500000006350412343704354013652 0ustar dasdas00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it!