Posts tagged: python

All posts with the tag "python"

36 posts latest post 2025-09-08
Publishing rhythm
Sep 2025 | 2 posts

I’m building a few FastAPI apps to throw in docker and run on my homelab… I wanted to add healthchecks and here’s a simple way to do it

Make sure to install curl in the dockerfile (near the top for effeciency)

# Install curl with minimal dependencies
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

Then I recommend making compose files even for single image deployments

services:
  app:
    build: .
    volumes:
      - type: bind
        source: .
        target: /app
    environment:
      - PYTHONPATH=/app
      - DOCKER_ENV=true
      - UV_VIRTUALENV=/opt/app-env
    user: "1000:1000"
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s

Then finally you’ll need a /health endpoint


@app.get("/health", response_class=HTMLResponse)
async def health_check():
    """
    A health check endpoint that returns a status message.
    """
    return "<html><body><h1>Service is healthy</h1></body></html>"

Switching from AltaCV to RenderCV for my Resume

I was using a fun LaTex-based project for managing my resume called AltaCV. I loved the customization and was familiar with Tek from school. However, I update my resume so infrequently that anytime I’d hop back to it I’d have to remember how to work with Tex and that was frustrating as I’ve lost touch with it over the years. Scrolling GitHub treding repos I saw RenderCV which let’s me just use YAML to write my resume and then it compiles to Tek through Python. There’s a sister project to make your own using this very easly call rendercv-pipeline. I forked that repo and translated my tek resume to the YAML. The included theme is nice enough is YAML is much easier to maintain long-term. My resume is behind a private GH repo but the example from rendercv-pipeline is here on GitHub

Modal Labs

Playing around with Modal Labs One of the first things I tried was a regular cron job… This can get deployed with This function gets deployed as an app that I conveniently call (as far as I can tell the app name can be anything - as I add functions to this same app and deploy with the same name to get a new version) Notice that this also is an example of giving access to a secret - defined in the Modal Labs dashboard We can take a look at the apps running at https://modal.com/apps I then added another function to experiment with custom container images and saw then that Modal will just slap a new version on anything provisioned with the same name (intuitive enough for sure) so when I add functions to my.py script and run over and over, the app called in the Modal apps dashboard just gets a new version This means I can spin up several instances of functionally the same app but with different names/versions etc… Q: Maybe there’s gitops or policy stuff builtin to app names then? I needed…

Logging instead of printing

I am trying to adopt logger.debug instead of print but ran into a confusing thing in ipython during Advent of Code… I riddled by script with logger.debug (yes after setting logging.setLevel('DEBUG')) but in ipython none of my log messages showed up!

import logging

logger = logging.getLogger(__name__)
logger.setLevel("DEBUG")

Turns out what I was missing was a call to basicConfig

import logging

# forget this and your messages are in the ether! or at least not seen in ipython...
logging.basicConfig()

logger = logging.getLogger(__name__)
logger.setLevel("DEBUG")

Bonus

Want your new messages to show up while iterating on something without killing the ipython kernel?

from importlib import reload
reload(logging) # to make sure you get new log messages you add while developing!

Benchmark your disks with fio

Intro I use ZFS at home in my homelab for basically all of my storage… Docker uses ZFS backend, all my VMs have their images in their own zfs datasets, and all my shares are ZFS datasets. I love ZFS but my home hardware presently is the opposite of expensive or new… Thankfully I’ve had a lot of my orginal homelab simply given to me but the cost of this is that I didn’t put my machines together, I didn’t choose the disks, and I definitely didn’t do the research I would’ve otherwise done had I bankrolled my server personally… The Problem # I run on basically all my machines and for the longest time I have been seeing big time issues. Now, since everything was free I’ve largely been able to ignore that however I’m now after some better performance which I think means new hardware! Here is a random screenshot of my glances homepage at time of writing - The only major load on my server is some transcoding (about 60% CPU utilization)… As you can see… there’s a lot of issues and I don’t even…
import this; print(this); print("what is taking so long black!!")

Mike Driscoll recently tweeted about making colored out with pandas DataFrames and I just had to try it for myself

Use Case

First though… why? My biggest use case is a monitoring pipeline of mine… The details aside, the output of my pipeline is a dataframe where each row has information about a failed pipeline that I need to go look into. I dump that result to a simle html file that’s hosted on an internal site and the file is updated every couple of hours. Adding some colored indicators automatically to the rows to help me assess severity of each record would be a handy way to quickly get an understanding the state of our pipelines.

How?

The docs for the applymap method state simply:

Apply a CSS-styling function elementwise.

Updates the HTML representation with the result.

So we can write a function that returns color: {color} based on the dataframe values and when we drop that dataframe to html we’ll have some simple css styling applied automagically!

By default the function will be applied to all columns of the dataframe, but that’s not useful if the columns are different types which is usually the case. Luckily there is a subset keyword to only apply to the columns you need!

Consider my example

sandbox   main via 3.8.11(sandbox) ipython
❯ df = pd.read_csv("cars.csv")

sandbox   main via 3.8.11(sandbox) ipython
❯ def mpg_color(val: float):
...:     color = "red" if val < 21 else "green"
...:     return f"color: {color}"

sandbox   main via 3.8.11(sandbox) ipython
❯ df.style.applymap(mpg_color, subset="mpg").to_html("color.html")

I want to quickly see if the mpg is any good for the cars in the cars dataset and I’ll define “good” as better than 21 mpg (not great I know but just for the sake of discussion…)

The function returns an appropriate css string and after I style.applymap on just the mpg column we get this!

  Unnamed: 0 mpg cyl disp hp drat wt qsec vs am gear carb
0 Mazda RX4 21.000000 6 160.000000 110 3.900000 2.620000 16.460000 0 1 4 4
1 Mazda RX4 Wag 21.000000 6 160.000000 110 3.900000 2.875000 17.020000 0 1 4 4
2 Datsun 710 22.800000 4 108.000000 93 3.850000 2.320000 18.610000 1 1 4 1
3 Hornet 4 Drive 21.400000 6 258.000000 110 3.080000 3.215000 19.440000 1 0 3 1
4 Hornet Sportabout 18.700000 8 360.000000 175 3.150000 3.440000 17.020000 0 0 3 2

I have list [True, False, False, True] and another list [1, 2, 3, 4] and a use case where I want to filter list 2 based on list 1 to remove values that line up with the element False in list 1…. so the outcome will be [1, 4]. list(compress(list2, list1)) will do it. As long as you can create a mask for the filter than itertool.compress will be your friend!

I just started using FastAPI for a home project and needed to pass back a dynamic number of values from a form rendered with jinja…

Dynamic Values

The jinja templating for rendering HTML based on something like a python iterable is nice and easy

data is the result of a database query, and item is each row, so the dot notation is the value of each column basically in that row

<form method="post">
  {% for item in data %}
    <div class="form-check ">
        <input class="form-check-input"  name="item_{{ item.id }}" id="{{ item.name }}" value="{{ item.id }}" type="checkbox">
        <label class="form-check-label" for="{{ item.id }}" > Label for: {{ item.name }} </label>
    </div>
  {% endfor %}

<button type="submit" class="submit btn btn-xl btn-outline-danger" >Remove</button>
</form>

This form generates a row with a checkbox for every item in data (in my case each item is an existing row in my table). it?

The way to pass back all those values is pretty straight forward (after hours of messing around that is!)

# I hate it when tutorials don't show ALL relevant pieces to the blurb
import starlette.status as status
from fastapi import APIRouter, Depends, Form, Request
from fastapi.encoders import jsonable_encoder
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.templating import Jinja2Templates
from sqlalchemy.orm import Session

from app.session.session import create_get_session

router = APIRouter()
templates = Jinja2Templates(directory="templates/")

@router.post("/my_route/do_something_with_form", response_class=HTMLResponse)
async def delete_rows(
    request: Request,
    db: Session = Depends(create_get_session),
):
    form_data = await request.get_form()
    data = jsonable_encoder(form_data)
    # data = {"item_1": 1, "item_2": 2, ... "item_N": N}
    return RedirectResponse("/", status_code=status.HTTP_302_FOUND)

We await request.get_form() and after encoding the data we get a dictionary with key/value pairs of the name/value from the form!

This took me quite a long time to figure out in part because most of the Google-able resources are still on Flask…

Dynamic-Form-Values-With-Jinja-And-Fastapi

I’m currently working on a self-hostable wish list app using FastAPI so we can finally drop Amazon forever. (The lists funcionality has been super handy for sharing holiday gift ideas with the famj!) FastAPI FastAPI is an amazing framework for quickly building APIs with Python. I will have a slightly longer post about my brief experience with it coming later… Jinja, Forms, and FastAPI One of the last things I needed to figure out in my app was how to generate a form in a Jinja template with a dynamic number of inputs and then pass all the inputs to the backend to perform a database operation (my exact case was removing rows from a table). Explicit Values # The way to pass back explicit variables is really easy… Our form would look like this (I’m using bootstrap CSS) So what is this? This form will have 2 rows with the lables you see in and checkboxes that when checked would have the value in each line. So our backend might looks something like this… I’m keeping all the imports and stuf…