thecodex.expert · The Codex Family of Knowledge
Tier 2 · Intermediate · Python Project

Weather CLI

Fetch live weather for any city from a web API and display it. Your first program that talks to the internet.

🧠 Teaches how to think spoonfed, every age Last verified:

1 The Problem

We want a command-line tool that takes a city name, asks a weather API over the internet, and shows the temperature and conditions. It teaches the single most important real-world skill beyond the basics: calling a web API — sending a request and parsing the JSON response.

Where this shows up: almost every modern app talks to APIs — payments, maps, weather, social, AI. Fetching data from a service and parsing its JSON is the daily reality of software development.

2 How to Think About It

Think about the request-response cycle, before any code:

The plan — in plain English
1. Build a URL for the weather API, including the city. → 2. Send an HTTP request to it. → 3. The API replies with JSON (structured text). → 4. Pull out the temperature and conditions from that JSON. → 5. Show them. The new idea is talking to a server and reading its answer.

Ask for a city

Build API URL

Send HTTP request

Receive JSON response

Pull out temp and conditions

Show the weather

3 The Build — explained part by part

Here is the complete weather tool. Each part is explained below. (It uses urllib, which is built into Python — no install needed.)

Pythonweather.py
import json
import urllib.request
import urllib.parse

def get_weather(city):
    # Open-Meteo's free geocoding + forecast (no API key needed).
    geo_url = "https://geocoding-api.open-meteo.com/v1/search?" + \
        urllib.parse.urlencode({"name": city, "count": 1})
    with urllib.request.urlopen(geo_url) as response:
        geo = json.load(response)

    if not geo.get("results"):
        return None

    place = geo["results"][0]
    lat, lon = place["latitude"], place["longitude"]

    forecast_url = "https://api.open-meteo.com/v1/forecast?" + \
        urllib.parse.urlencode({"latitude": lat, "longitude": lon,
                                "current_weather": "true"})
    with urllib.request.urlopen(forecast_url) as response:
        data = json.load(response)

    return data["current_weather"]

if __name__ == "__main__":
    city = input("City: ")
    weather = get_weather(city)
    if weather:
        print(f"Temperature: {weather['temperature']}C")
        print(f"Wind: {weather['windspeed']} km/h")
    else:
        print("City not found.")
What each part does — in plain words
import urllib.request — Python’s built-in way to fetch URLs. (The popular requests library is nicer, but urllib needs no install, so we start here.)

urllib.parse.urlencode({...}) — safely turn our parameters (the city name) into the ?name=London&count=1 part of a URL, escaping spaces and special characters.

urllib.request.urlopen(url) — send the HTTP request and get the response. json.load(response) parses the JSON reply into Python dictionaries and lists.

two requests — first we look up the city’s latitude/longitude (geocoding), then we fetch the forecast for those coordinates. Chaining API calls like this is very common.

data["current_weather"] — reach into the parsed JSON to pull out the part we want, then read temperature and windspeed from it.
Common mistakes — and how to avoid them
✗ Building the URL by gluing strings — a city like “New York” with a space breaks the URL.
✓ Use urllib.parse.urlencode to escape parameters safely.
✗ Assuming the city is always found — an unknown city has no results and crashes.
✓ Check if not geo.get("results") and handle the missing case.
✗ Calling the live API in tests — slow, flaky, and fails offline.
✓ Test parsing against saved sample responses instead.

4 Test & Prove Each Part

Network calls are unpredictable, so we test the parsing logic against a saved sample response — not the live API.

Parsing a sample response extracts the temperature
A missing-city response is handled (returns None)
The coordinates are read correctly from geo data
Pythontest_weather.py
def parse_current(data):
    """Extract the current weather block from a forecast response."""
    return data.get("current_weather")


def parse_place(geo):
    """Extract lat/lon from a geocoding response, or None."""
    if not geo.get("results"):
        return None
    place = geo["results"][0]
    return place["latitude"], place["longitude"]


def test_parse_temperature():
    """Temperature is read from the sample response."""
    sample = {"current_weather": {"temperature": 18.5, "windspeed": 10}}
    assert parse_current(sample)["temperature"] == 18.5


def test_missing_city():
    """An empty geocoding result returns None."""
    assert parse_place({"results": []}) is None


def test_coordinates():
    """Lat and lon are read from geo data."""
    geo = {"results": [{"latitude": 51.5, "longitude": -0.1}]}
    assert parse_place(geo) == (51.5, -0.1)

Run with pytest -v. We never call the real API in tests — networks are slow and unreliable. Instead we test parsing against saved sample data. This is exactly how professionals test API code.

5 The Interface

INPUTcity namea place to look up
What it expects
City: London
OUTPUTweathertemperature and wind
What it returns
Temperature: 14.2C
Wind: 11 km/h

6 Run It & Automate It

Run it locally
python3 weather.py
Type a city to see its live weather. Needs an internet connection. Uses the free Open-Meteo API (no key required).

Jenkins runs the tests automatically — each line explained below.

What you should see when it works
Terminala real run
City: London
Temperature: 14.2C
Wind: 11 km/h
If it breaks — how to fix it
🚨 URLError or a timeout.
No internet, or the API is down. Check your connection.
🚨 KeyError: 'current_weather'.
The response shape differs from expected — print the raw JSON to inspect it.
GroovyJenkinsfile
// Jenkinsfile — automated tests on every change.
pipeline {
    agent any
    stages {
        stage('Get the code') { steps { checkout scm } }       // download the code
        stage('Install') { steps { sh 'python3 -m venv venv && . venv/bin/activate && pip install pytest' } }  // test tool
        stage('Test') { steps { sh '. venv/bin/activate && pytest -v' } }   // run every test
    }
    post {
        success { echo 'All tests passed.' }
        failure { echo 'A test failed — look above.' }
    }
}
🎯 Try this next — make it yours
  1. Use requests. Install the friendlier requests library and simplify the code. (Teaches: third-party packages.)
  2. Forecast. Show the next few days, not just now. (Teaches: reading more of the JSON.)
  3. Cache results. Save recent lookups to avoid repeat calls. (Teaches: caching.)
What you learned
You learned to call a web API: build a URL, send a request, and parse the JSON response — even chaining two calls together. And you learned to test API code by parsing saved samples, not hitting the live service. This is the gateway to all modern, connected software. Related: json, HTTP.