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

Proxy object API #159

Closed
pipermerriam opened this issue Sep 11, 2019 · 2 comments
Closed

Proxy object API #159

pipermerriam opened this issue Sep 11, 2019 · 2 comments

Comments

@pipermerriam
Copy link
Member

What was wrong?

Here's a fictional API I've been thinking about.

from lahja import proxy
from my_code import Animal

@dataclass
class Animal:
    name: str
    sound: str

class ProxyAnimal(proxy.Proxy[Animal]):
    name = proxy.Text()
    get_sound = proxy.Method()
    wait_something = proxy.AsyncMethod()

# to serve it:
cat = Animal(name='cat', sound='meow')
with proxy.serve_object(endpoint, ProxyAnimal, cat) as obj_id:
    ...

# to use it from across process boundaries
cat = ProxyAnimal(endpoint, obj_id)

I think this achieves the following goals.

  • low boilerplate
  • allows for ABC ObjectAPI equivalent implementations via proxy objects which should mean they are functionally interchangeable.

How can it be fixed?

Not sure. One major problem is the need to interact with the event bus from both async and synchronous call sites. Things like object properties and non-async methods will mean that we'll be entering the endpoint APIs from a synchronous context. We can likely pull something off using threads and broadcast_nowait. It might be nice to have a way to interact with EndpointAPI.request as well since everything from the client side will be a request/response.

The Text() and Method() APIs would likely be descriptors which know how to access the endpoint through their parent object.

A meta-class is probably going to be needed to programmatically generate event types but it may be possible to do get by with something simpler like a set of builtin events for the request/response or an internal event.

@cburgdorf
Copy link
Contributor

class ProxyAnimal(proxy.Proxy[Animal]):
    name = proxy.Text()
    get_sound = proxy.Method()
    wait_something = proxy.AsyncMethod()

This ☝️ looks like it would mess with typing, no? How would mypy know the correct types of get_sound or wait_something.

I'm also not entirely sold yet if optimizing for proxy objects is the best way forward. While it is convenient, I'm scared it would lead us back to the RPC style approach to multiprocessing that we replaced with a more explicit, event-based paradigm.

I think that there may be some middle ground where the object on the consumer side is still hand crafted in the sense of it being:

  • not derived from some proxy type
  • implemented with real methods

I also think that there isn't evens so much inconvenience on the consumer side today. E.g. this is an RPC module in Trinity fetching the network id via the event bus.

    async def version(self) -> str:
        """
        Returns the current network ID.
        """
        response = await self.event_bus.request(
            NetworkIdRequest(),
            TO_NETWORKING_BROADCAST_CONFIG
        )
        return str(response.network_id)

And this could even still get simpler with #90

I think it is more the serving side that needs more of our attention. Currently in Trinity we have the PeerPoolEventServer that receives requests and delegates them somewhere and then wraps the response and sends it back. I think that is the bigger inconvenience we have today. Some of this may be easier if our PeerPool would just natively speak lahja so that there would simply be no need for a PeerPoolEventServer to step in. But it could also be that we could come up with better tools that would just simplify the job of replaying events onto some methods and returning the results (and capturing exceptions).

To recap my thoughts, I'm not saying I'm against proxies, I'm just saying maybe there are other options that we should check before.

@pipermerriam
Copy link
Member Author

I'm no longer convinced this is a good idea.

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

2 participants