The Dispatcher¶
The Dispatcher object in PyOSC is what routes incoming OSC messages based on their address. It's implementation is simple and efficient, allowing you to easily register handler based on methods, addresses and pydanntic models.
Creating a Dispatcher¶
Dispatchers no longer need to be explicitly created, as they are now automatically initialized when you create a Peer object. However, if you need to create one manually, you can do so like this:
Registering Handlers¶
The default handler
The concept of a default handler hasn't existed for a few versions at this point, so I think it is appropriate to remove it from the documentation. However, I will at this time reccomend against using the /* wildcard as a replacement, as it is against specification and this workaround will be removed in version 2.0.0. If you do need this functionality, I will be implementing a replacement method for this functionality in a comming release pre 2.0.0, as 2.0.0 will contain multiple Major breaking changes.
Registering Handlers¶
In addition to the default handler, you can register handlers for specific OSC addresses. This allows you to drop or ignore messages from certain addresses, as well as process information from others.
Here's an example of what a handler specific addresss might look like:
def ping_handler(message): #(1)!
print(f"Received a ping message! {message.args}") #(2)!
peer.dispatcher.add_handler("/test/out/ping", ping_handler) #(3)!
- The
ping_handlerfunction is defined to handle messages sent to the/test/out/pingaddress. - Inside the handler, we print out the arguments of the received ping message.
- The
ping_handlerfunction is registered to handle messages sent to the/test/out/pingaddress using theadd_handlermethod of thedispatcherattribute of thepeerobject.
Dispatch handlers can be registered and destroyed at any time, this is because when a message is received, the dispatcher checks against it's list of handlers to see if any match the incoming message's address. As long as the handler is registered when the message arrives, it will be called.
Validators¶
When registering handlers, you can also provide an optional validator. A validator is a pydantic model that is used to validate incoming messages before they are passed to the handler. If the message does not conform to the validator, it will be rejected and not processed by the handler.
Here's an example of registering a handler with a validator:
from pydantic import BaseModel #(1)!
class PingResponse(BaseModel): #(2)!
args: tuple[OSCString]
@property
def message(self) -> str:
return self.args[0].value #(3)!
def ping_handler(message: PingResponse): #(4)!
print(f"Received a ping message with response: {message.message}")
peer.dispatcher.add_handler("/test/out/ping", ping_handler, validator=PingResponse) #(5)!
- We import
BaseModelfrom thepydanticlibrary to create a validator model. - We define a
PingResponsemodel that specifies the expected structure of the incoming message - We define a property
messagethat extracts the string value from the first argument of the message. - The
ping_handlerfunction is defined to accept aPingResponseobject, which will be validated before being passed to the handler. - The
ping_handlerfunction is registered to handle messages sent to the/test/out/pingaddress, this time with thePingResponsemodel as its validator.
Examples¶
Here is a complete example that demonstrates the use of a dispatcher with both a default handler and an address-specific handler with a validator:
from pyosc import Peer, OSCMessage, OSCModes, OSCFraming, OSCString
from pydantic import BaseModel
peer = Peer(
"127.0.0.1",
3032,
mode=OSCModes.TCP,
framing=OSCFraming.OSC11,
) #(1)!
class PingResponse(BaseModel):
args: tuple[OSCString] #(2)!
@property
def message(self) -> str:
return self.args[0].value #(3)!
def default_handler(message):
with open("messages.log", "a") as log_file:
log_file.write(f"Received a message on address:{message.address} with args: {message.args}\n") #(4)!
def ping_handler(message: PingResponse):
print(f"Received a ping message with response: {message.message}") #(7)!
peer.dispatcher.add_default_handler(default_handler)
peer.dispatcher.add_handler("/test/out/ping", ping_handler, validator=PingResponse) #(5)!
peer.start_listening() #(6)!
- A
Peerobject is created to handle OSC messages over TCP on localhost at port 3032. - A
PingResponsepydantic model is defined to validate incoming ping messages. - A property
messageis defined to extract the string value from the first argument of - The
default_handlerfunction logs all received messages to a file namedmessages.log. - The
ping_handlerfunction is registered to handle messages sent to the/test/out/pingaddress, with thePingResponsemodel as its validator. - The
start_listeningmethod is called on thePeerobject to begin receiving messages. - The
ping_handlerfunction prints the response message when a valid ping message is received.