Nick's Notes

Occasionally I drink too much coffee and get the idea that I’m going to write down some of my thoughts. This is where they end up - my hodgepodge of projects, research, and interesting articles I’ve read.

I loosely group the content into three categories:

  • Blog Posts: Links to articles or how-tos on other sites.
  • Notebooks: Ongoing research and notes on topics I have found interesting.
  • How-tos: Somewhat completed projects.

Latest Blog Posts

Python Project Setup (2026 Edition)


A few years back I linked to Alex Mitelman’s Python best practices post as my go-to reference for new project setup. The tooling has moved on quite a bit since then, so here’s my updated take.

The new stack is three tools, all from Astral: uv, ruff, and ty. They’re fast, they integrate cleanly with each other, and they collapse a lot of the old toolchain into a much simpler setup.

uv — dependency management, environments, everything

uv replaces pyenv + pip + venv in one shot. It handles Python version management, virtual environments, dependency locking, and running commands in the right environment.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# install uv
curl -LsSf https://astral.sh/uv/install.sh | sh

# start a new project
uv init my-project
cd my-project

# add dependencies
uv add requests
uv add --dev ruff ty pytest

The uv run command is the other key habit to pick up — it activates the right environment automatically so you don’t have to think about it:

1
2
uv run python my_script.py
uv run pytest

ruff — linting and formatting

ruff replaces flake8, isort, black, and a handful of other linters, and it does it fast. The --fix flag handles most issues automatically.

1
2
uv run ruff check --fix .
uv run ruff format .

Configure it in pyproject.toml:

1
2
3
4
5
6
[tool.ruff]
line-length = 88
target-version = "py312"

[tool.ruff.lint]
select = ["E", "F", "I"]  # pycodestyle, pyflakes, isort

ty — type checking

ty is Astral’s new type checker. It’s still early but it’s fast and editor-friendly. Drop it into the same uv run workflow:

1
uv run ty check
1
2
[tool.ty]
# pyproject.toml config here when needed

The short version

1
2
3
uv init my-project && cd my-project
uv add --dev ruff ty pytest
uv run ruff check --fix . && uv run ruff format . && uv run ty check

Everything lives in pyproject.toml, no separate config files needed. It’s a much cleaner setup than what I was doing a few years ago.

Comments
Linux Bonding with VLANs


Every time I set up network bonding on a Linux box I end up digging through the same stack of man pages and blog posts from 2014. This is my attempt to write it down somewhere useful.

The Setup

The goal is straightforward: combine two physical NICs into a bonded interface for redundancy (or throughput), then layer VLAN-tagged sub-interfaces on top of the bond. This is bread-and-butter stuff for any server attached to a managed switch.

1
2
3
4
5
Physical: eth0, eth1
  └── bond0 (active-backup or 802.3ad)
        ├── bond0.10  (VLAN 10 — management)
        ├── bond0.20  (VLAN 20 — production)
        └── bond0.30  (VLAN 30 — storage)

Configuring the Bond

Load the bonding kernel module and create the bond interface:

1
2
3
4
5
modprobe bonding
ip link add bond0 type bond mode active-backup
ip link set eth0 master bond0
ip link set eth1 master bond0
ip link set bond0 up

For active-backup, one interface carries traffic and the other sits idle as a hot standby — the switch doesn’t need any special config. If you want 802.3ad (LACP) for active/active, your switch ports need to be configured with a port-channel on the other end.

To make the bond persist across reboots on a RHEL/Rocky system, drop a file in /etc/NetworkManager/system-connections/ or use nmcli:

1
2
3
nmcli con add type bond con-name bond0 ifname bond0 bond.options "mode=active-backup,miimon=100"
nmcli con add type ethernet ifname eth0 master bond0
nmcli con add type ethernet ifname eth1 master bond0

Adding VLANs

With the bond up, adding VLANs is just a matter of creating tagged sub-interfaces:

1
2
3
4
5
6
ip link add link bond0 name bond0.10 type vlan id 10
ip link add link bond0 name bond0.20 type vlan id 20
ip link set bond0.10 up
ip link set bond0.20 up
ip addr add 10.10.10.5/24 dev bond0.10
ip addr add 10.20.20.5/24 dev bond0.20

With nmcli:

1
2
nmcli con add type vlan con-name bond0.10 ifname bond0.10 dev bond0 id 10 ip4 10.10.10.5/24 gw4 10.10.10.1
nmcli con add type vlan con-name bond0.20 ifname bond0.20 dev bond0 id 20 ip4 10.20.20.5/24

Checking Your Work

A few commands worth bookmarking:

1
2
3
4
5
6
7
8
# Bond status — shows active slave, link state, MII status
cat /proc/net/bonding/bond0

# Verify VLAN sub-interfaces
ip -d link show bond0.10

# Check which slave is active
ip link show master bond0

The cat /proc/net/bonding/bond0 output is especially handy — it’ll tell you which interface is currently active, when the last failover happened, and the MII polling interval.

One Gotcha

If you’re running firewalld, new VLAN interfaces won’t automatically inherit a zone. Assign them explicitly or traffic will hit the default zone (usually public, which drops most things):

1
2
firewall-cmd --zone=trusted --add-interface=bond0.20 --permanent
firewall-cmd --reload

That one burned me for longer than I’d like to admit.

Comments
Python Typer


I love writing a good cli tool, but it’s no fun parsing switches and arguments. Python’s click is a solid choice, but lately I’ve been loving typer. Typer is a super easy-to-use library, built on top of click.

Typer uses method variables and type hints to generate and parse the command line arguments for you, and even generates the help context. It’s practically magic.

check it out:
Typer

Comments
Python Best Practices


I always forget which tools are the latest and greatest to use when I’m setting up a new Python project. Alex Mitelman published a really helpful summary here:


Python Best Practices for a New Project in 2021

Comments
Control Chromecast From Linux / BSD / MacOS


$ export URL="http://ccmixter.org/contests/freestylemix/hisboyelroy/freestylemix_-_hisboyelroy_-_Revolve.mp3"
$ playerctl -p My_Chromecast open "$URL"

This will play a song on the Chromecast.

Ok, that’s awesome. chromecast_mpris, by Alex DeLorenzo

Comments

More...