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

More advanced Transport API example. #984

Closed
tomchristie opened this issue May 22, 2020 · 3 comments
Closed

More advanced Transport API example. #984

tomchristie opened this issue May 22, 2020 · 3 comments
Labels
docs Changes to the documentation wontfix

Comments

@tomchristie
Copy link
Member

tomchristie commented May 22, 2020

Now that we've got a really tightly spec'ed Transport API, one useful thing we can do for the ecosystem is show our users how to do nifty things like adding in extra behaviour.

For example, here's a simple implementation of a throttle...

class ThrottleTransport:
    """
    A custom httpx transport that adds some basic rate-throttling functionality.

    transport = ThrottleTransport('100/minute', **connection_pool_options)
    client = httpx.Client(transport=transport)
    """

    def __init__(self, throttle, **kwargs):
        self.pool = httpcore.SyncConnectionPool(**kwargs)
        self.history = []
        
        # Parse the thottle, which should be a string, like '100/minute'.
        count, _, duration = throttle.partition('/')
        self.max_in_history = int(count)
        self.cutoff = {'second': 1.0, 'minute': 60.0, 'hour': 3600.0}[duration]

    def request(method, url, headers, stream, timeout):
        now = time.time()

        while len(self.history) >= self.max_in_history:
            expiry = now - self.cutoff

            # Expiry old entries in the history.
            self.history = [timestamp for timestamp in self.history if timestamp > expiry]
            
            # Sleep for a bit if we've exceeded the throttle rate.
            if len(self.history) >= self.max_in_history:
                time.sleep(0.1)
                now = time.time()

        self.history.append(now)
        return self.pool.request(method, url, headers, stream, timeout)
    
    def close(self):
        self.pool.close()

I've not tested this out, just looks about right to me. Might well be worth making sure I've got this right, and then adding it as an example in the docs?

Slightly dependant on #983, since really users need that in order to configure the httpcore pool nicely.

@tomchristie tomchristie added the docs Changes to the documentation label May 22, 2020
@tomchristie tomchristie changed the title More advance Transport API example. More advanced Transport API example. May 22, 2020
@florimondmanca
Copy link
Member

florimondmanca commented May 24, 2020

I'm thinking it might also be possible to build features like retries (#784) and others using this sort of style.

However, I'm still not certain these shouldn't belong to some sort of "transport wrapper" concept…

E.g. throttling (or retries, or any sort of "modify the request/response flow" thing) should be fairly independent of how the actual request is made - we can also want to throttle requests to ASGI apps, over UDS, to a proxy, etc.

(And… this is when I come back again with my "middleware" idea? 😅 #345)

@lazka
Copy link

lazka commented May 29, 2020

I'm thinking it might also be possible to build features like retries (#784) and others using this sort of style.

Transparent caching would also be nice imho, using HTTP cache headers and connecting it to something like https://github.com/argaen/aiocache

@stale
Copy link

stale bot commented Feb 20, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Feb 20, 2022
@stale stale bot closed this as completed Mar 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Changes to the documentation wontfix
Projects
None yet
Development

No branches or pull requests

3 participants