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

(Optionally) Return futures #327

Open
rgbkrk opened this issue Sep 26, 2014 · 25 comments
Open

(Optionally) Return futures #327

rgbkrk opened this issue Sep 26, 2014 · 25 comments

Comments

@rgbkrk
Copy link

rgbkrk commented Sep 26, 2014

In order for me to use this in tornado and other asynchronous frameworks, I either have to make direct calls to the Docker API (using an async http client) or wrap a threadpool executor around this.

I'd like to see if there's a way for us to provide both a synchronous and asynchronous version of docker-py.

@rgbkrk rgbkrk changed the title Return futures (Optionally) Return futures Sep 26, 2014
@rgbkrk
Copy link
Author

rgbkrk commented Sep 28, 2014

I wrote a terrible way to do this when working with Tornado:

class AsyncDockerClient():
    '''Completely ridiculous wrapper for a Docker client that returns futures
    on every single docker method called on it, configured with an executor.
    If no executor is passed, it defaults to ThreadPoolExecutor(max_workers=2).
    '''
    def __init__(self, docker_client, executor=None):
        if executor is None:
            executor = ThreadPoolExecutor(max_workers=2)
        self._docker_client = docker_client
        self.executor = executor

    def __getattr__(self, name):
        '''Creates a function, based on docker_client.name that returns a
        Future. If name is not a callable, returns the attribute directly.
        '''
        fn = getattr(self._docker_client, name)

        # Make sure it really is a function first
        if not callable(fn):
            return fn

        def method(*args, **kwargs):
            return self.executor.submit(fn, *args, **kwargs)

        return method

The right way to do this would be to just extend your base class and change the underlying client.

/cc @minrk

@leonmax
Copy link

leonmax commented Mar 4, 2015

+1

2 similar comments
@mastak
Copy link

mastak commented Mar 12, 2015

+1

@jdavanne
Copy link

jdavanne commented Jun 4, 2015

+1

@SakuraSound
Copy link
Contributor

SakuraSound commented Apr 29, 2016

Is there any status for adding futures support to docker-py? Would like to see a solution within docker-py that isn't just utilizing asyncio.run_in_executor(INSERT DOCKER-PY COMMAND).

EDIT: perhaps this could be a separate project? The "simple" path would be duplication of code, but i could see how that would be messy...

@zbyte64
Copy link

zbyte64 commented May 17, 2016

Here is my approach using requests_futures:

from requests_futures.sessions import FuturesSession
from docker import Client

class AsyncIOClient(FuturesSession, Client):
    def request(self, *args, **kwargs):
        return FuturesSession.request(self, *args, **kwargs).result()

Returning a future at the request level mucks up everything but at least this way I get co-operative threading. Edit: My understanding is that this is still blocking at the IO level.

@cecton
Copy link
Contributor

cecton commented May 23, 2016

Hi everyone. I'm thinking of this too and wonder if it would be a good idea to have the Client class to use aiohttp's Session class instead of requests?

The idea is that requests doesn't handle async, so it would be nice to get async methods for every action. (Does that make make sense?)

@graingert
Copy link
Contributor

graingert commented Jun 2, 2016

https://pypi.python.org/pypi/yieldfrom.requests is one option

The solution for all these blocking libraries, is to use async/await coroutines, but they will not be usable until 2020-01-01 when all currently supported Python versions without async/await are both EOL.

Ideally requests, docker-py, etc etc will create an asyncio version of their libraries then import it into a legacy blocking version.

@graingert
Copy link
Contributor

graingert commented Aug 24, 2016

twisted now supports asyncio, and so you can create code that works with https://github.com/twisted/treq instead. This way there can be a blocking api that wraps the Twisted calls, and there can be a non-blocking api using Twisted or asyncio via Twisted on Python 3.

@graingert
Copy link
Contributor

However it might be better to use the https://sans-io.readthedocs.io/ approach, then people can plug their own session in.

@SakuraSound
Copy link
Contributor

I think a BYO-Netlib could be good way to deal with this, though I know that would be a much larger effort. My guess is that we would still need an asynchronous docker client class to enable async/await for all the functions

@graingert
Copy link
Contributor

you could use pypi https://pypi.python.org/pypi/deferred just need to poke @hawkowl or @glyph to upload a fresh one

@graingert
Copy link
Contributor

graingert commented Aug 24, 2016

this way you can write a blocking request session wrapper that takes deferreds and the aiodocker-py library can replace it

@glyph
Copy link

glyph commented Aug 24, 2016

If this would be useful we'd be quite happy to help out; just let us know!

However, Deferred is a pretty slow-moving abstraction, so you're unlikely to need any new features; you can just pip install it and try it out as-is.

@graingert
Copy link
Contributor

graingert commented Aug 25, 2016

@glyph well I'm really looking to have the async/await support here: twisted/twisted#489

also wheels

@glyph
Copy link

glyph commented Aug 25, 2016

@graingert Oh! Well in that case, yes, we should definitely do that :).

@graingert
Copy link
Contributor

graingert commented Aug 25, 2016

and maybe get it off the launchpad and into Git(Hu|La)b.

@glyph
Copy link

glyph commented Aug 25, 2016

It's currently on GH as https://github.com/mikeal/deferred

@glyph
Copy link

glyph commented Aug 25, 2016

(The plan is to move it into the Twisted org, of course.)

@graingert
Copy link
Contributor

ah I found it on https://launchpad.net/deferred

@glyph
Copy link

glyph commented Aug 25, 2016

Yeah that was the original location.

@oberstet
Copy link

oberstet commented Jun 5, 2017

FWIW, there is also txaio which would allow an implementation that runs on both Twisted and asyncio (user choice) ..

Disclosure: I am affiliated with txaio

@graingert
Copy link
Contributor

@cecton have a look at this: https://github.com/aio-libs/aiodocker

@cecton
Copy link
Contributor

cecton commented Jun 12, 2017

@graingert Thx! I already saw it (too late) but I wanted something closer to docker-py and more official (because then Docker maintain it).

But my approach is bad too... I end up adapting all the code to put "async" and "await" keywords everywhere. This is definitely not what I wanted. I wanted some kind of wrapper on docker-py that would make the call asynchronous but now I kinda realize it's impossible or I totally missed something.

.... actually.... I just realize now that it is under aio-libs so it's maintain by the devs of aiohttp. 😕 great lol

@cecton
Copy link
Contributor

cecton commented Aug 8, 2017

To whom is interested: I managed to wrap docker-py: https://github.com/tenforce/docker-py-aiohttp

It can certainly be ported to other asyncio libraries using the same principles. The core of the principles are located here: https://github.com/cecton/requests2aiohttp

In brief: I made a class that is meant to override requests.sessions.Session. All the HTTP calls are redirected to aiohttp and I have a custom Response class that wraps aiohttp's Response class. Its main purpose is to have a common API with requests.sessions.Session so the docker-py code can use it the same way.

It's not state of the art but I added integration tests for the same Docker versions tested by docker-py and it seems very stable.

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

10 participants