Skip to content
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

Simplify/eliminate callRemote #10

Open
micolous opened this issue Apr 26, 2014 · 2 comments
Open

Simplify/eliminate callRemote #10

micolous opened this issue Apr 26, 2014 · 2 comments

Comments

@micolous
Copy link

I was thinking it may be better to eliminate the use of callRemote in txdbus. This would mean that D-Bus remote objects are first-class Python objects, which makes RPC interfaces much simpler, and don't need any glue to talk over the D-Bus.

How I've implemented this in my own code is with something like this:

class DBusRemoteWrapperMethod(object):
    """
    Wrapper for methods for interface.callRemote
    """
    def __init__(self, obj, methname):
        self._obj = obj
        self._methname = methname


    def __call__(self, *args, **kwargs):
        return self._obj.callRemote(self._methname, *args, **kwargs)


class DBusRemoteWrapper(object):
    """
    Wrapper for interfaces that makes everything a callRemote.

    """
    def __init__(self, obj):
        self._obj = obj

    def __getattr__(self, name):
        return DBusRemoteWrapperMethod(self._obj, name)

# (setup reactor here, omitted for simplicity)

@defer.inlineCallbacks
def main():
    conn = yield client.connect(reactor, 'session')
    obj = yield conn.getRemoteObject(DBUS_SERVICE, DBUS_PATH)

    # Add callRemote wrapper
    api = DBusRemoteWrapper(obj)

    # Now we can execute methods directly on the DBus object as if it was
    # a normal Python class...
    yield api.my_method()
    result = yield api.do_other_things('bar', 123)

There's a some limitations to my implementation as it stands:

  • Members are generated dynamically, and it does not check or expose the org.freedesktop.DBus.Introspection interface in order to be smart about this (and populate things like __all__.
  • This doesn't support wrapping org.freedesktop.DBus.Properties to Python properties.
  • This doesn't support handling other interfaces of the object.

There's one major limitation to my implementation as it stands is that it can't handle org.freedesktop.DBus.Properties interface wrapping to a Python property. This would require a little bit more glue in order to handle this.

With o.fd.DB.Properties implementation, it would allow even more interaction like a normal Python object:

api.MyProperty = 6
while api.BottleCount > 0:
  api.BottleCount -= 1

This would make txdbus work a little more like python-dbus, and other RPC mechanisms like Python's XMLRPC module in the standard library (ServiceProxy).

@cocagne
Copy link
Owner

cocagne commented Apr 28, 2014

Michael,

You are quite correct that callRemote could be eliminated in favor of
providing wrapper objects that simulate native object usage. It was,
however, an intentional design decision to not do this. There are two
primary reasons for not doing so in txdbus. First is that I was attempting
to provide an interface similar to that of Twisted's long-standing
Perspective Broker API. The second, and more important reason, is that I
agree with Perspective Broker's rationale for intentionally avoiding the
native-looking APIs for remote objects.

Remote methods do not behave in the same manner as native methods. If
nothing else, network connection failures can occur at any time and robust
code must account for the fact that all remote calls can fail with TimeOuts
and/or ConnectionLost exceptions. The visual distinction between
"o.callRemote('foo', 1,2,3)" and "o.foo(1,2,3)" helps to remind readers of
your code that you're dealing with remote objects and that the rules are
different. Admittedly, it's somewhat less convenient during the initial
implementation but the maintenance benefits, in my opinion, more than make
up for it.

Cheers,

Tom

On Sat, Apr 26, 2014 at 1:25 AM, Michael Farrell
[email protected]:

I was thinking it may be better to eliminate the use of callRemote in
txdbus. This would mean that D-Bus remote objects are first-class Python
objects, which makes RPC interfaces much simpler, and don't need any glue
to talk over the D-Bus.

How I've implemented this in my own code is with something like this:

class DBusRemoteWrapperMethod(object):
""" Wrapper for methods for interface.callRemote """
def init(self, obj, methname):
self._obj = obj
self._methname = methname

def __call__(self, *args, **kwargs):
    return self._obj.callRemote(self._methname, *args, **kwargs)

class DBusRemoteWrapper(object):
""" Wrapper for interfaces that makes everything a callRemote.
"""
def init(self, obj):
self._obj = obj

def __getattr__(self, name):
    return DBusRemoteWrapperMethod(self._obj, name)

(setup reactor here, omitted for simplicity)

@defer.inlineCallbacksdef main():
conn = yield client.connect(reactor, 'session')
obj = yield conn.getRemoteObject(DBUS_SERVICE, DBUS_PATH)

# Add callRemote wrapper
api = DBusRemoteWrapper(obj)

# Now we can execute methods directly on the DBus object as if it was
# a normal Python class...
yield api.my_method()
result = yield api.do_other_things('bar', 123)

There's a some limitations to my implementation as it stands:

  • Members are generated dynamically, and it does not check or expose
    the org.freedesktop.DBus.Introspection interface in order to be smart
    about this (and populate things like all.
  • This doesn't support wrapping org.freedesktop.DBus.Properties to
    Python properties.
  • This doesn't support handling other interfaces of the object.

There's one major limitation to my implementation as it stands is that it
can't handle org.freedesktop.DBus.Properties interface wrapping to a Python
property. This would require a little bit more glue in order to handle this.

With o.fd.DB.Properties implementation, it would allow even more
interaction like a normal Python object:

api.MyProperty = 6while api.BottleCount > 0:
api.BottleCount -= 1

This would make txdbus work a little more like python-dbus, and other RPC
mechanisms like Python's XMLRPC module in the standard library (
ServiceProxy).


Reply to this email directly or view it on GitHubhttps://github.com//issues/10
.

@socketpair
Copy link

@micolous, you may simplify your code using return functools.partial(self._obj.callRemote, name) instead of return DBusRemoteWrapperMethod(self._obj, name)
and also I recommend you to check name.startswith('_') and raise appropriate error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants