Skip to content

lib/msgpackrpc: Require a name when binding callables. #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 38 additions & 10 deletions lib/msgpackrpc/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,38 @@
import gc


class Adder:
def __init__(self):
pass

def add(self, a, b):
logging.info(f"add({a}, {b}) is called")
return a + b
def add(a, b):
logging.info(f"add({a}, {b}) is called")
return a + b


def sub(a, b):
logging.info(f"sub({a}, {b}) is called")
return a - b


class Foo:
def __init__(self, name):
self.name = name

def add(self, a, b):
logging.info(f"{self.name}.add({a}, {b}) is called")
return a + b

def sub(self, a, b):
logging.info(f"{self.name}.sub({a}, {b}) is called")
return a - b


class Adder:
def __init__(self):
pass

def __call__(self, a, b):
logging.info(f"Adder({a}, {b}) is called")
return a + b


if __name__ == "__main__":
# Configure the logger.
# All message equal to or higher than the logger level are printed.
Expand All @@ -34,9 +52,19 @@ def sub(a, b):
# Create an RPC object
rpc = msgpackrpc.MsgPackRPC()

# Register objects or functions to be called by the remote processor.
rpc.bind(Adder())
rpc.bind(sub)
# Register remote functions.
rpc.bind("sub", sub)
rpc.bind("add", add)

# Register a callable object.
rpc.bind("adder", Adder())

# Register full objects. The following binds all public methods of an object to their
# respective qualified names. For instance, `foo1`'s methods will be bound to `foo1.add`
# and `foo1.sub`. Alternatively, bound methods can be registered individually, by calling
# bind on each method. For example, `rpc.bind("foo.add", foo.add)`.
rpc.bind("foo1", Foo("foo1"))
rpc.bind("foo2", Foo("foo2"))

# Start the remote processor and wait for it to be ready to communicate.
rpc.start(firmware=0x08180000, timeout=1000, num_channels=2)
Expand Down
33 changes: 17 additions & 16 deletions lib/msgpackrpc/msgpackrpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def __init__(self, streaming=False):
self.msgid = 0
self.msgbuf = {}
self.msgio = MsgPackIO() if streaming else None
self.servers = []
self.callables = {}

def _bind_callback(self, src, name):
if log_level_enabled(logging.INFO):
Expand Down Expand Up @@ -135,30 +135,31 @@ def _send_msg(self, msgid, msgtype, fname, fargs, **kwargs):
return Future(msgid, self.msgbuf, fname, fargs)

def _dispatch(self, msgid, fname, fargs):
func = None
retobj = None
error = None
for obj in self.servers:
if callable(obj) and obj.__name__ == fname:
func = obj
elif hasattr(obj, fname):
func = getattr(obj, fname)
if func is not None:
break

if func is not None:
retobj = func(*fargs)

if fname in self.callables:
retobj = self.callables[fname](*fargs)
else:
error = "Unbound function called %s" % (fname)

self._send_msg(msgid, _MSG_TYPE_RESPONSE, error, retobj)

def bind(self, obj):
def bind(self, name, obj):
"""
Register an object or a function to be called by the remote processor.
obj: An object whose methods can be called by remote processors, or a function.
Bind a callable or an object to a name.
name: The name to which the callable or object is bound.
obj: A callable or an object to bind to the name. If an object is passed, all of its
public methods will be bound to their respective qualified names.
"""
self.servers.append(obj)
if callable(obj):
# Bind a single callable to its name.
self.callables[name] = obj
else:
# Bind all public methods of an object to their respective qualified names.
for k, v in obj.__class__.__dict__.items():
if callable(v) and not k.startswith("_"):
self.callables[name + "." + k] = getattr(obj, k)

def start(self, firmware=None, num_channels=2, timeout=3000):
"""
Expand Down