Skip to content

Latest commit

 

History

History
170 lines (115 loc) · 7.69 KB

File metadata and controls

170 lines (115 loc) · 7.69 KB

Appendix A: Four Essential Packages to Know

Back in the venv appendix, we talked about pip install-ing packages other people have written. PyPI - the Python Package Index - hosts hundreds of thousands of these, but a small handful show up in an enormous share of real-world Python projects. Let’s meet four of the most popular: requests, pydantic, SQLAlchemy, and FastAPI.

We’re not going to make you an expert in any of these - that’s well beyond a beginner’s book. The goal here is just recognition: when you see one of these names later in your coding life, you’ll already know roughly what it does and why people reach for it.

requests: Talking to the Web

Almost every program eventually needs to fetch data from somewhere out on the internet - checking the weather, pulling stock prices, hitting some company’s API. Python’s built-in tools for this are clunky. requests makes it feel almost as simple as print().

pip install requests
import requests

response = requests.get("https://api.github.com")

print(response.status_code)  # -> 200, meaning "OK, success!"
print(response.json())        # -> a dictionary, parsed straight from the response

That .get() call reaches out over the internet and fetches whatever’s at that address - the same basic thing your web browser does every time you visit a page, just done from your own code. .status_code tells you whether it worked (200 means success; you’ll also see 404 for "not found," and others), and .json() conveniently turns the response straight into a Python dictionary, ready to use.

Sending data works about the same way, using .post() instead of .get():

new_player = {"name": "Sam", "health": 100}

response = requests.post("https://example.com/players", json=new_player)

print(response.status_code)  # -> 201, meaning "Created!"

If requests has one core lesson, it’s this: your program isn’t limited to the data sitting on your own computer. It can reach out and talk to almost anything on the web.

pydantic: Data You Can Trust

Remember type hints, from a few chapters back? We mentioned that Python doesn’t actually enforce them - they’re just polite suggestions. pydantic is a package built entirely around fixing that: it uses your type hints to actually validate data, at the moment your program receives it.

pip install pydantic

You describe your data’s shape once, as a class, using the same type hints you already know:

from pydantic import BaseModel

class Player(BaseModel):
    name: str
    health: int = 100

sam = Player(name="Sam", health=87)
print(sam.health)  # -> 87

That looks a lot like the classes from the Objects chapter, except we didn’t have to write our own __init__. BaseModel, the class we inherited from, builds one for us automatically, based entirely on the type hints we wrote.

Here’s the real payoff - what happens when the data is wrong:

bad_player = Player(name="Sam", health="a lot")
# pydantic.ValidationError: 1 validation error for Player
# health
#   Input should be a valid integer

pydantic looked at our hint, health: int, and refused to accept the string "a lot" in its place. Instead of your program silently accepting bad data and crashing later, somewhere confusing, pydantic stops it immediately, right at the door, with a clear error explaining exactly what went wrong.

SQLAlchemy: Talking to a Database

Most real programs need to remember things even after they stop running - player accounts, orders, inventory. That’s what a database is for. SQLAlchemy lets you work with a database using ordinary Python classes and objects, instead of writing raw database query language by hand.

pip install sqlalchemy
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import declarative_base, Session

Base = declarative_base()

class Player(Base):
    __tablename__ = "players"
    id = Column(Integer, primary_key=True)
    name = Column(String)
    health = Column(Integer)

engine = create_engine("sqlite:///game.db")
Base.metadata.create_all(engine)
Note
That __tablename__ looks a lot like the dunder methods from a few chapters back, but it isn’t one - it’s not a method Python itself ever calls. It’s just a special attribute name that SQLAlchemy itself looks for, as part of its own class-building machinery, to know what to name the table. Still handy to recognize the double-underscore naming pattern showing up again, in a different context.

Once that Player class exists, we can save new players and look them back up, all with ordinary-looking Python:

with Session(engine) as session:
    session.add(Player(name="Sam", health=100))
    session.commit()

    sam = session.query(Player).filter_by(name="Sam").first()
    print(sam.health)  # -> 100

Notice we never wrote a single line of raw database query syntax. SQLAlchemy translated session.query(Player).filter_by(name="Sam") into the appropriate database query behind the scenes, and handed us back a real Player object - the very same kind of class we defined ourselves, complete with a .health attribute we can just read.

FastAPI: Building Your Own Web API

requests let us call someone else’s web service. FastAPI flips that around - it’s for building your own. It’s one of the most popular ways to build a web API in Python today, largely because it leans so heavily on type hints and pydantic, both of which you already know.

pip install fastapi uvicorn
Note
uvicorn is a second package you install alongside FastAPI - it’s the actual program that runs your API and listens for incoming requests. FastAPI describes what your API does; uvicorn is what keeps it running.
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Player(BaseModel):
    name: str
    health: int = 100

@app.get("/")
def read_root():
    return {"message": "Welcome to the dungeon API!"}

@app.post("/players")
def create_player(player: Player):
    return {"greeting": "Welcome, " + player.name + "!"}

main.py

That @app.get("/") line is a decorator - something we haven’t covered in this book, but you can read it simply as "run the function right below this when someone visits this address." The second one, @app.post("/players"), does the same thing for incoming data - and notice the parameter, player: Player. That type hint isn’t just documentation here; FastAPI and pydantic team up to automatically validate every incoming request against our Player model, before our function even runs.

You’d start this API from a terminal with:

uvicorn main:app --reload

and FastAPI even generates interactive, browsable documentation for your API automatically, for free, just from your type hints - no extra work required.

How These Four Fit Together

Picture a simple game server. FastAPI handles incoming web requests; pydantic (working right alongside FastAPI) validates that the data in each request actually makes sense; SQLAlchemy saves and retrieves player data from a real database; and some other program, maybe even one you write yourself, uses requests to call your finished API from the outside, the exact same way it might call anyone else’s.

Four different jobs, four different packages - and every single one of them is built on ideas you already know: classes, type hints, dictionaries, and dunder-style naming conventions. That’s really the whole point of a solid beginner’s foundation: once you have it, even "advanced" packages like these turn out to be built from pieces you already recognize.