python data tools live

2025-09-23

This image is an inside-joke over at the office that I didn't want to forget about. Too many fun puns.

Uploaded image
We're doing an offsite soon and as a joke ... this was the poster that I designed for the event.

framework mechanical keyboard

2025-09-22

Ah stuff like this just really makes my day. It's a YT video about how you can use a framework laptop as a starting point and add a custom keyboard unto it. It really helps that framework is designed to be so repairable, because that also makes it much more hackable. Or to re-use the words from the author:

mechanical keyboard? yes please. gasket mounted? oh my. three rotary encoders? on a LAPTOP? need I say any more?

the Campus is a custom laptop built off the Framework platform, featuring a completely redesigned chassis to house a kailh choc powered, gasket mounted, enthusiast grade custom mechanical keyboard.

I'm still very happy with my current gear, but when it's time for something new it's really looking like framework is not just going to be more repairable ... but also way more fun.

ruff banned imports

2025-09-16

I was thinking about SQLAlchemy and how it has specific features for different backends. PostgresSQL has JSON support, for example, but Sqlite doesn't. So how might you make sure that your SQLAlchemy code only uses models that can be used across different backends?

Although it's probably not perfect, I did learn about banned imports via ruff. You can do something like this:

# pyproject.toml
[tool.ruff]
select = ["F", "E", "W", "TID"]  # TID enables banned imports

[tool.ruff.lint.flake8-tidy-imports]
banned-module-level-imports = [
    "sqlalchemy.dialects.postgresql",
    "sqlalchemy.dialects.mysql", 
    "sqlalchemy.dialects.oracle",
    "sqlalchemy.dialects.mssql",
    "sqlalchemy.dialects.sqlite",
]

# Or ban the entire dialects module
[tool.ruff.lint.flake8-tidy-imports.banned-api]
"sqlalchemy.dialects".msg = "Use only portable SQLAlchemy types, not dialect-specific ones"

This way, ruff will complain every time that you import from the dialects submodule of sqlalchemy.