Asynchronous HTTP Requests in Python
40 comments · 93% upvoted · source · other discussions
40 comments · 93% upvoted · source · other discussions
As a rule, async makes sense when you have tasks that require you to wait (e.g. network bound IO) and your program could be doing something useful in the meantime.
In this case, the task is an API request that mostly consists of waiting for a response. "something useful in the meantime" is initiating other API requests.
One thing to note is that API requests are often rate limited, meaning there is a hard limit on the amount of work you can get done with a single API key even if you're program is capable of processing more.
You may be able to get a little more (micro) optimization by sharing the client session, since it uses connection pooling. Recreating it for each call is a bit unnecessary.
[deleted]
Yup, I write a lot of API integrations (mainly sites with no public API) and not hammering the site you're accessing is the golden rule. I'm not familiar with the NBA stats API but I'm guessing it's meant to be for their website and mobile apps only, not for mass scraping; bashing it with dozens of requests at once, especially when each request apparently takes a lot of resources on the server to complete, is a great way to see that API put behind more obfuscation or removed altogether in favour of embedding the content in HTML, or more efficient blocking of unauthorized clients than just checking the user agent, which the NBA site obviously does.
magic. do you know a nice guide or explanation for python async stuff? i find it hard to wrap my mind around it
What do you have a hard time grasping exactly?
not so much the concept of async but the syntax.
async def is designating a coroutine. This is awaitable, which means that every time you call it using await foo()you are signalling to the event loop that a context switch occurs and you give up execution control to whatever task is ready to be run next while you wait for your awaitable.
async for is basically the same thing, except you're saying that the for loop can yield a result at any time and that you want to wait to iterate over the results until you have something to iterate over, although it might not be complete just yet.
async with is saying "hey, give up control of the event loop until I can actually acquire my context manager"
Most of the rest of asyncio follows this sort of thing, it winds up looking like synchronous code but acts like it's threaded.
I'm relatively new to asyncio and I'm not sure I like it too much, so someone chime in with corrections if I'm wrong
Yeah the syntax is definitely wonky.
Not even Armin Ronacher understands the asyncio stuff... It looks like a proper mess.
async def
Defines a coroutine (which is just a function).
await and yield from
These are the points in your code where you release control to the event loop. Usually you await or yield from another coroutine, so that is also added to the event loop.
I wonder what the runtime would be with multiple threads. In my opinion, that's really what you should be comparing against when it comes to convincing people to use async for io-bound operations.
Is it really the same outcome? How did you test this? Are you able to access players near the end of the list right after you issue the asynchronous call?
Maybe a silly question but how does this differ from Eventlet / Gevent / Twisted (maybe I can't even compare these with each other?)
My knowledge on these libraries is still rather superficial so I can't truly appreciate the depths and uniqueness of each library but if someone would like to explain to me why this library described in the post is worthwhile than please do.
EDIT: Also came across this SO post explaining some differencces between the libraries and other related ones. http://stackoverflow.com/questions/1824418/a-clean-lightweight-alternative-to-pythons-twisted
Good simple example.
Can't help but notice though the complete lack of mention why this is faster and what the implications are.
In both functions you're sending all the requests at once. In get_players that's only one request so it barely matters (in fact you don't really need to async that one at all). In get_player that means you're opening an unknown number of connections simultaneously, which may cause all sorts of issues. Most importantly the API may decide you're violating its policy agains DoS/Spam and ban you.
The most ugly thing there is
asyncio.gather(*
(get_player(*args) for args in player_args))
I am using the requests library through locust.io and in this configuration the requests library is partially async at least.
It even mentions it in the requests library docs under advanced usage, it won't wait for a response unless you try to look at reponse.content for example.
Personally, I just use libcurl and let it deal with the details. I don't trust anyone to implement HTTP correctly.
This is a contradiction. It doesn't add up that you "don't trust anyone to implement HTTP correctly" and also "use libcurl and let it deal with the details". That implies some extent of trusting libcurl. Even then, whether or not they implement HTTP correctly doesn't really answer the question of how to handle async requests in Python.
I'm actually writing API load tests where I need to script fake users doing stuff and making choices as they get through the loadtest script and do any HTTP requests asyncrhonously as a web browser would, which is where locust comes in.
I would hate to write loadtests that would use curl for this sort of thing and have it driving 500+ users, with locust this just works for me as this is exactly what locust was designed for.
Go would be excellent for this sort of thing too, though I am using locust because it's more mature.
Finally.
_seemethere [OP] 16 points · Feb 2017 · github.com/seemethere
Format strings are the best! Makes development a ton easier.
[deleted] 8 points · Feb 2017
[deleted]
kankyo 6 points · Feb 2017
Yea
[deleted] 7 points · Feb 2017
[deleted]
slapec 8 points · Feb 2017
The funny thing is that I tried to be sarcastic.
spidyfan21 1 points · Feb 2017
Poe's law.
slapec 1 points · Feb 2017
TIL
[deleted] 4 points · Feb 2017
[deleted]
Corm 3 points · Feb 2017
It's the timeless argument of adding a small bit of complexity to the language (new f strings) for a small quality of life increase vs not doing it.
I have no opinion here. I like f strings for when I have several variables, but I also don't like adding stuff to the language if there's not much gain. I'm 50% for and 50% against it in this case.
As far as it not being any better though, it's pretty much there for stuff like this:
Which is much easier to read than if you used + or .format. But again, I'm not saying that the pros outweigh the added complexity
[deleted] 1 points · Feb 2017
[deleted]
Corm 2 points · Feb 2017
Oh, yeah, I agree
gandalfx -1 points · Feb 2017
There are actually no examples in that entire code sample where f-Strings are shorter than simple concatenation.