pyliblo icon indicating copy to clipboard operation
pyliblo copied to clipboard

can add_method take a regex to specify components from the OSC path ?

Open stuaxo opened this issue 9 years ago • 7 comments

I'm used to url handling in stuff like django, where components can be passed as variables:

I'm constructing osc paths like this: path = "/vis/smilies/{control_number}/amount".format(control_number=x)

is there a way to specify something like this in add_method, similar to how django urls work with regexes ?

django url soec https://docs.djangoproject.com/en/1.8/topics/http/urls/

stuaxo avatar Nov 08 '15 13:11 stuaxo

I put together some toy code to do something similar in an ipython notebook -

import re

urlspecs = []

def add_regex_method(urlspec, f):
    urlspecs.append((urlspec, f))

def dispatch_url(url):
    matched = False
    for spec, f in urlspecs:
        matches = re.match(spec, url)
        if matches is not None:
            d = matches.groupdict()
            f(url, **d)
            matched = True
    if not matched:
        pass  ## call handle for nonhandled urls


def handle_amount_url(url, n=None):
    print("got amount url: %s n=%s" % (url, n))

add_regex_method("/smilies/(?P<n>.+)/amount", handle_amount_url)

dispatch_url("/smilies/10/amount")
dispatch_url("/notmatch")  # nothing happens

stuaxo avatar Nov 08 '15 16:11 stuaxo

output of above code:

got amount url: /smilies/10/amount n=10

stuaxo avatar Nov 08 '15 16:11 stuaxo

For my own uses, I've extended server - like this - though would be nice to have similar functionality in the core

example server extended to handle regex matching osc paths

#!/usr/bin/env python

from __future__ import print_function

import re

import liblo, sys


class OSCServer(liblo.Server):
    # extend OSCServer to have regex matching of osc paths
    def __init__(self, *args, **kwargs):
        liblo.Server.__init__(self, *args, **kwargs)
        self.add_method(None, None, self.dispatch_msg)
        self.pathspecs = []

    def dispatch_msg(self, path, args, types, src):
        """
        regex matching for osc messages
        """
        matched = False
        for spec, t, f in self.pathspecs:
            matches = re.match(spec, path)
            if matches is not None:
                d = matches.groupdict()
                f(path, *args, **d)
                matched = True
        if not matched:
            print("not matched ", path)
            pass  ## call handle for nonhandled urls

    def add_regex_method(self, spec, t, f):
        self.pathspecs.append((spec, t, f))

# create server, listening on port 1234
try:
    server = OSCServer(1234)
except liblo.ServerError as err:
    print(err)
    sys.exit()


def handle_amount(path, amount, n=0):
    print("%s set amount %s" % (n, amount))

def handle_alpha(path, amount, n=0):
    print("%s set alpha %s" % (n, amount))

def master_alpha(path, amount):
    print("set master alpha %s" % amount)

server.add_regex_method("/vis/entities/(?P<n>.+)/amount", 'f', handle_amount)
server.add_regex_method("/vis/entities/(?P<n>.+)/alpha", 'f', handle_alpha)
server.add_regex_method("/vis/master/alpha", 'f', master_alpha)

# loop and dispatch messages every 100ms
while True:
    server.recv(100)

stuaxo avatar Nov 08 '15 16:11 stuaxo

The OSC spec actually has pattern matching that works the other way round, i.e. messages can contain wildcards, while the addresses registered with an OSC server don't. These wildcards are more glob-like, and I don't know if liblo fully implements the OSC spec, but pattern matching is supported.

The problem I see here is that you can't really match wildcards against wildcards, at least not in general (and in a sane way). Currently pyliblo leaves all the pattern matching and message dispatching to liblo, and the only way to add regex matching (without modifying liblo itself) would be through a "wildcard" callback like in your example.

I think this would be a useful addition to pyliblo (arguably more practical than the current pattern matching in liblo), but we should decide on clear semantics of which messages would be supported, and which matching mechanism takes precedence.

dsacre avatar Nov 16 '15 11:11 dsacre

I'm also always a bit surprised, that pyliblo/liblo doesn't support more flexible dispatch. Some time ago I implemented an extended form of regular expresssion based dispatching here:

https://github.com/SpotlightKid/python-rtmidi/blob/master/examples/osc2midi/oscdispatcher.py

It works a bit similar to Django URL dispatching. You define a "pattern" patterns = [(regex, typecodes, handler, paramsdict)] and add that to a dispatcher dispatcher = OSCDispatcher(patterns, search_ns=myhandlermodule). See the main function of the module for an example. What's special about this module is that it allows you to convert part of the OSC address pattern into arguments of your handler function, optionally with a conversion function.

SpotlightKid avatar Nov 16 '15 15:11 SpotlightKid

Hello, sorry I only saw this 5 years later, but I just came across it and have now added pattern matching in method names to liblo. https://github.com/radarsat1/liblo/commit/efea904078f1fde89c93d1d880c2d2180098c221

radarsat1 avatar Aug 30 '20 18:08 radarsat1

Looking back, I ended up writing a pattern matching dispatcher thingy and using it -

https://github.com/stuaxo/mnd It filtered arguments to functions with a syntax based on Djangos querys.

But if I play with OSC again this will be a good option.

Feel free to close this unless anyone else feels it should stay open.

stuaxo avatar Aug 30 '20 19:08 stuaxo