Calling
aiohttp.ClientSession
in_post
and_get
is really bad. Don't do this.There is no
main
function, this just makes the code harder to convert into a setuptools application.All these
functools.partial
only hinder readability rather than enhance it.Just make a class.
The code isn't very functional. And I can only imagine what a purist would have to say about
_send_weather
. There are so many side effects in one little, hard to read, function.There is no separation of concerns,
_find_city
and_city_weather
are solely around getting a forecast, whilst_notify
is sending a notification. You've mangled them into one_send_weather
function. Not great.Since the code isn't really functional, and this is possibly the worst project to learn functional programming on. Let's just make it so that the code is easy to read.
Make a
WeatherForecast
class.At instantiation it only takes a
session
.
This is so we can remove all thosepartial
s withself.get
.Include
get
into this class, however without the session creation part.Include
find_city
asfind_cities
, and don't mutate the output.Include
city_weather
.Change
send_weather
toget_weather
and delegate calls tofind_city
andcity_weather
.It is not allowed to
print
here. All it does is get the weather for the wanted city.This is a helper function and so mutates the values to get the two other function to work, and to get the output as we desire. Nothing else.
Remove the session creation part from
post
.Make a function
notify_user
that notifies the user ofcity
'sweather
.This is as simple as using two f-strings, to build the url and the message.
Change
_send_weather
so that it takes a session, city and user.To make the function easy to read, we are limited to three things in the function.
- Calling another function.
- Performing a simple
if
else
to control data flow. - Formatting output to user - basic f-strings.
Here's how I performed the above. Which is as functional as your code. And you could argue that my functions are more pure than yours, as get_weather
only gets weather. notify_user
notifies the user. And send_weather
only print
s and delegates to other impure functions. None of them are pure and can't be because they all have side effects. Which pretty much makes implementing this in 'pure FP' a joke.
import asyncio
from functools import partial
import aiohttp
SUCCESSFUL_PUSH = {'deleted': True} # Value specific to fake push service
class WeatherForecast:
def __init__(self, session):
self.session = session
async def get(self, headers, url):
async with self.session.get(url, verify_ssl=False) as response:
return await response.json()
async def find_cities(self, city):
return await self.get(f"https://www.metaweather.com/api/location/search?query={city}")
async def city_weather(self, city_id):
return await self.get(f"https://www.metaweather.com/api/location/{city_id}")
async def get_weather(self, city_name):
cities = await self.find_city(city_name)
if not cities:
return None
forecast = await self.city_weather(cities[0]["woeid"])
return forecast["consolidated_weather"][0]["weather_state_name"]
async def post(session, url, data):
async with session.post(url, json=data, verify_ssl=False) as response:
return await response.json()
def notify_user(session, city, weather, user):
return await post(
session,
f"https://api.keen.io/dev/null?user={user}",
{"message": f"Current weather in {city}: {weather}"},
)
def send_weather(session, city, user):
weather = await WeatherForecast(session).get_weather(city)
if weather is None:
print("Could not get forecast")
else:
reply = notify_user(session, city, weather, user)
if reply == SUCCESSFUL_PUSH:
print(f"Current weather in {city}: {weather}")
else:
print(f"Push notification to {user} failed: {reply}")
async def main():
headers = {"Content-Type": "application/json"}
async with aiohttp.ClientSession(headers=headers) as session:
send_weather(session, "Glasgow", "Bob")
if __name__ == "__main__":
asyncio.run(main())