Update: fpl (0.6.0) is now asynchronous!

in #fpl6 years ago (edited)






https://github.com/amosbastian/fpl

What is fpl?

It's a Python wrapper for the Fantasy Premier League, which is a fantasy football game - "a game in which participants assemble an imaginary team of real life footballers and score points based on those players' actual statistical performance or their perceived contribution on the field of play. " At the time of writing this post there are more than 6 million teams entered, which includes loads of people on Steem who are participating in Steem's very own league (see here).


Alongside working on FPL Plus I've also had some other ideas for FPL related projects. For example, one of my Reddit bots, which has been running for ~3 years now, is closed source because I'm so embarrassed by the code. While also having a few bugs, it is also dependent on other sources other than the FPL API, which isn't great, as sometimes they mess up and in turn it messes my bot up. Because of this I've wanted to rewrite it and add some new features, however before doing this I wanted to make fpl asynchronous. The biggest reason for this is that when using synchronous requests it takes a really long while to update, for example, all the players (I'll show a comparison further down) - for some of the features I want to add to the bot this is unacceptably slow. So I set out to convert everything (apart from the CLI) to use asynchronous requests using aiohttp!

  • Use aiohttp instead of requests for asynchronous HTTP requests
  • Add kwarg return_json to return dict instead of object
  • Refactor all functions and classes
  • Class properties are now the same as the FPL API (e.g. instead of user.name it would be user.player_first_name) to keep a consistency between dict and objects
    *Convert tests to use pytest-aiohttp for async support

https://github.com/amosbastian/fpl/pull/19

Using aiohttp and asyncio

I asked a couple of people if they had experience writing asynchronous Python packages, but most people didn't. In the end I found a Python package called aiohttp, which seemed to be exactly what I needed. Before I was using the requests package, while also passing the responsibility of sending requests to the classes themselves, as shown in the image below:



An example of the FPL class in version 0.5.5

As you can see the _get_information() function is called and then used to assign values to the class' properties. I didn't think it was a good idea to have this, especially when I decided to add the option to return a dict instead of a class, since I was assigning the values to properties of a different name (e.g. first_name != player_first_name) which would cause inconsistency.

So, for the asynchronous version of fpl I decided to send a GET request from the functions in the FPL class and pass it to e.g. the User class instead.



An example of the FPL class in version 0.6.0

I also refactored some functions so that they use other functions of the FPL class, with asyncio, to speed up the requests significantly. For example, the get_players() function now doesn't send any requests itself, but creates a list of get_player() coroutines and uses asyncio.ensure_future() and asyncio.gather().



The updated functions get_player() and get_players()

To show the speedup I created two code snippets, one using the synchronous version and one using the asynchronous version, that both do the same thing. I timed both of them, and as you can see, the speedup is crazy!

fpl 0.5.5fpl 0.6.0
1m35.663s0m1.313s

Other than just making it everything asynchronous a lot of work has gone into refactoring everything to, in my opinion, make fpl more intuitive and accessible. I don't really want to go too deep into this, as the most important feature of this update is its asynchronisity, but I hope that people who have used fpl before this update will notice this!

I've also decided to remove the MongoDB support, because after thinking about it I felt it didn't really belong in the package and was too opinionated. Furthermore it was only really there to speed up the package itself by saving information to a database, but with the new asynchronous version it's already fast enough and this isn't needed anymore.

Asynchronous unit testing?!

When I first started with converting everything from synchronous to asynchronous I thought I could just keep the unit tests I had already written. I was also not passing the session as a class parameter, which was working fine at first, but then I found out it was giving warning messages that had to do with unclosed client sessions. Once I started passing it as a parameter the unit tests broke and I didn't really know what to do. After many hours trying to fix this, I resorted to asking on StackOverflow.

Thankfully someone responded, but unfortunately it meant rewriting most of the unit tests to work with pytest-aiohttp (a plugin for pytest) instead of unittest, which I thought would be much more difficult than it ended up being. Instead of having a setUp() function in each test class I ended up using pytest's fixtures like so:



which allows you to access e.g. the fpl variable from your tests like so:



which works great! In the end I'm really happy I ended up switching to pytest as it works great, looks much more clean and seems easier to use. I would definitely recommend switching to it for anyone who still uses unittest!

Usage & installation

You can simply install it with pip:

pip install fpl

From version 0.6.0 it requires Python 3.6+, so make sure you are using an up to date version of Python!

Contributing

I will be updating the documentation and adding contributor guidelines soon, but if you play FPL (or don't) and know Python, then don't hesitate to contact me on Discord (Amos#4622)!

Sort:  

This contribution was a pleasure to review and perfect in all cases. That being said, there is not much to say on my side. This was also a good learning experience for me to read the code regarding converting synchronous packages into the asynchronous model.


Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post, click here.


Need help? Chat with us on Discord.

[utopian-moderator]

Thanks a lot for your kind words Emre, it means a lot to me coming from a Python wizard like yourself! Although I'm not 100% sure I've implemented everything how it's meant to be implemented (I never am), I'm happy with how it turned out. Learning JavaScript recently also sort of gave me a better understanding of how to implement it, which I had never expected lol.

I'll be posting about it on the FantasyPL subreddit soon (also once I update the bot and add new features), so hopefully I'll find some other developers who can help me out / people to suggest features. Recently there have been quite a few developers on there posting about their projects, and maybe they'll even join Utopian one day (fingers crossed)!

Thank you for your review, @emrebeyler! Keep up the good work!

I had a look at aoihttp in the past.
It seemed like a nice library but our main issue is that it required Python 3.5.4 or newer which is not available on the distro we had to use (CentOS 7)

I was looking at other packages for asynchronous requests since I was thinking about keeping it compatible with Python 2.7, 3.3 etc. but in the end I thought sod it, I will just use the most recent technology instead. The only reason it doesn't work with Python 3.5 is the yield in the tests as far as I can tell, which I could maybe resolve in the future though (not exactly sure).

Congratulations @amosbastian! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :

You received more than 8000 as payout for your posts. Your next target is to reach a total payout of 9000

Click here to view your Board
If you no longer want to receive notifications, reply to this comment with the word STOP

To support your work, I also upvoted your post!

Support SteemitBoard's project! Vote for its witness and get one more award!

Hi @amosbastian!

Your post was upvoted by @steem-ua, new Steem dApp, using UserAuthority for algorithmic post curation!
Your post is eligible for our upvote, thanks to our collaboration with @utopian-io!
Feel free to join our @steem-ua Discord server

Hey, @amosbastian!

Thanks for contributing on Utopian.
Congratulations! Your contribution was Staff Picked to receive a maximum vote for the development category on Utopian for being of significant value to the project and the open source community.

We’re already looking forward to your next contribution!

Get higher incentives and support Utopian.io!
Simply set @utopian.pay as a 5% (or higher) payout beneficiary on your contribution post (via SteemPlus or Steeditor).

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!

starting FPL, I think we will know what FPL is

Hi, @amosbastian!

You just got a 0.08% upvote from SteemPlus!
To get higher upvotes, earn more SteemPlus Points (SPP). On your Steemit wallet, check your SPP balance and click on "How to earn SPP?" to find out all the ways to earn.
If you're not using SteemPlus yet, please check our last posts in here to see the many ways in which SteemPlus can improve your Steem experience on Steemit and Busy.

Coin Marketplace

STEEM 0.17
TRX 0.13
JST 0.027
BTC 60777.85
ETH 2609.63
USDT 1.00
SBD 2.65