Pretty cool video on the complexity of one of the greatest songs in Sesame Street. It's amazing just how intricate the musicianship is in this!

Roboforming

Sep 18, 2023

The latest Smarter Every Day video is engineering on an entirely different level. The math, software, and other challenges this company face are tremendously complex. It's amazing that people get to do this for a living! I bet we hear a lot more about this technology in the future; this seems like a promising area of development and manufacturing.

Here's another thing that linting taught me recently: Python's str.startswith() method, and str.endswith() as well, takes a tuple as the first parameter! This makes checking for multiple options really simple:

# Verbose way of writing it
if (mystring.startswith('c.') or mystring.startswith('m.') or mystring.startswith('s.')):
    ...

# Easier way
if (mystring.startswith(('c.', 'm.', 's.'))):
   ...

I didn't realize the language allowed this!

As I mentioned previously, I'm now using Ruff to lint my Python projects. Several linter warnings continually crop up in my code, which I find interesting, so I thought I'd highlight a few of them (there are plenty that I'm leaving out; I apparently write fairly crude code by these linters' standards).

missing-trailing-comma
This is a common recurrence in places where I'm setting up a dict for something:

mydict = {
    'posts': some_queryset.all(),
    'today': Date.today()  # Missing a trailing comma
}

if-expr-with-false-true
This pops up on occasion for me, though not terribly often. I apparently easily forget about the not operator.

return False if self.errors else True

# The above is a little more legible if we use:
return not self.errors

superfluous-else-return
I was surprised that this occurred so often in my code. Removing these cases flattens the code somewhat, which is a new practice I'm trying to engrain into my programming habits.

if (is_ajax_request(request)):
    return HttpResponseForbidden('Forbidden')
else:  # This isn't needed
    return redirect(reverse('home'))

# The above looks better as:
if (is_ajax_request(request)):
    return HttpResponseForbidden('Forbidden')

return redirect(reverse('home'))

explicit-f-string-type-conversion
This warning taught me something I didn't know about f-strings; namely that explicit conversion flags are available. Also that the conversions I was making were mostly not necessary in the first place.

error = f"Part not owned by {str(self.part_owner)}!"

# Better:
error = f"Part not owned by {self.part_owner!s}!."

# Best:
error = f"Part not owned by {self.part_owner}!."

type-comparison
Again, I was surprised by how often I do this. Base types can (and often are) subclassed, so it's better to use isinstance() than type is.

if (type(loader) is list):
    return error_response(loader)

# Better:
if (isinstance(loader, list)):
    return error_response(loader)

Running the linting frameworks has taught me a fair amount about my programming habits, and has also informed me about various aspects of the language. I recommend running linters if you don't already, and I highly recommend Ruff!

Linting With Ruff

Sep 8, 2023

I enjoy using linting frameworks for the code I write, primarily employing flake8 for my Python code, which is about 90% of what I write these days. Recently, however, I saw news on Ruff, a new linting framework written in Rust that is orders of magnitude faster. It's so fast that the entire CPython repository, which contains over 1200 files, can be linted from scratch in only 0.29 seconds. Several testimonial quotes in Ruff's README attest to this blazing speed:

Ruff is so fast that sometimes I add an intentional bug in the code just to confirm it's actually running and checking the code. - Sebastián Ramírez, creator of FastAPI

Just switched my first project to Ruff. Only one downside so far: it's so fast I couldn't believe it was working till I intentionally introduced some errors. - Timothy Crosley, creator of isort

Another benefit on top of its speed is the near-parity it brings with Flake8, which is nice. There are still a number of formatting rules from the pycodestyle package that haven't been implemented, which is an annoyance, but there's an active issue tracking the progress on that front.

To top it all off, Ruff includes rules from dozens of Flake8 plugins, most of which I've never run. Enabling all of them in my projects has been humbling, to say the least, but I'm learning a ton of improved practices from doing so. I don't always agree with some of the rules, and have disabled a number of rule sets that annoy me, but it's been an interesting learning process.

In the coming days I'll be writing about a few of the sloppy practices that this framework has pointed out in my code, so stay tuned.

My wife made these the other night for a dinner with neighbors and they turned out terrific. In fact, they're likely better than the snickerdoodle recipe that I posted several years ago! The cookies in this recipe end up way softer, and are just as flavorful. Highly recommended! Note that this recipe was taken from an external website, which I'm transcribing here for both simplicity and posterity.

Makes 36 cookies in about 20 minutes.

Dough

  • 2-3/4 cups all-purpose flour
  • 2 tsp cream of tartar
  • 1 tsp baking soda
  • 1/2 tsp salt
  • 1 cup unsalted butter, just softened
  • 1-1/2 cups sugar
  • 2 eggs
  • 1 tsp vanilla extract

Coating

  • 1/3 cup sugar
  • 2 tbsp cinnamon

Instructions

  1. Preheat oven to 350 degrees F.
  2. In a large bowl, mix together flour, cream of tartar, baking soda, and salt together. Set aside.
  3. In a stand mixer, cream together butter (barely softened) and sugar. Add eggs and vanilla and blend well.
  4. Add dry ingredients to wet ingredients and mix well.
  5. In a small bowl, combine remaining ⅓ cup sugar and 2 tablespoons cinnamon.
  6. Use a small cookie scoop to scoop out dough, roll into a ball and then roll into the cinnamon sugar mixture- twice.
  7. Place 2 inches apart on an ungreased cookie sheet.
  8. Bake for 8 – 10 minutes. Let sit on the cookie sheet for a few additional minutes before removing to a wire rack to cool.

Until recently, I knew next to nothing about Alice Cooper's music. I'd heard School's Out and No More Mister Nice Guy on the radio, but that was the extent of my knowledge. Alice is back in the news with a forthcoming album, so I figured I'd check out some of his music. I'm glad I did! Lots of his albums are particularly good, including Killer (I can't embed the videos here on this site, stupidly, so the link to the album playlist will have to suffice). There are some great tracks on this album: Halo of Flies (my favorite), Under My Wheels, and Dead Babies.

Billion Dollar Babies is an equally as good (if not better) album, as is his first solo effort Welcome to My Nightmare. I recommend checking them all out if you're not familiar.

The Operations Room

Apr 30, 2023

The Operations Room channel on YouTube has been killing it for a while now. Their top-down, animated videos show some of the most important battles and events in history. I've learned a ton about events in World War 2, Vietnam, the Gulf war, and even interesting isolated events. Their latest video, on the battle of Alligator Creek at Guadalcanal, is fascinating and I've linked it below. This is definitely a channel to follow; there's lots of terrific content.

We recently bought a new home and have been doing a number of updates. One thing in dire need of updating are the thermostats, which are original to the house (1992). The existing units were clunky, not very user friendly, and not particularly sensitive to temperature change.

I decided to purchase the ecobee smart thermostat, and was particularly motivated when our energy provider recently offered a deep discount on the units. I picked up a couple of them and updated the upstairs unit earlier today (I'll tackle the downstairs unit another day).

Because my setup didn't have a C-wire (for continuous power), the install process required that I installed a power-extender kit, which comes in the box. This involved adjusting the thermostat wires inside the HVAC unit, which was a little daunting at first. This process, however, turned out to be easier than I initially thought.

The entire install process took about an hour or so do, and I now have a modern, easy to use setup. One of the benefits of these units is that they have optional sensors you can buy to place in other rooms, which help the thermostat better control the temperature across the whole house. I plan on picking up one of these eventually to put in our bonus room, which seems to be hotter than the rest of the house on average.

I'm enjoying the DIY aspect of home ownership these days. I've learned a lot, am gaining new skills, and I'm saving a little on having to hire someone to do the work.

Magnet Sweeper

Apr 11, 2023

We recently had our roof replaced, which resulted in a ton of debris in the yard. One of our primary concerns with this was the number of loose nails and screws, which pose a hazard to both our car tires and our feet. Scanning through Home Depot's website, I found this magnet sweeper, which only set me back $12.

The second photo above shows just how many nails, screws, and other debris I was able to pick up with this thing. Wow! There are a few drawbacks to this tool. First, the handle is a little short for my taste. Second, the magnets could stand to be stronger, but they did a decent job. And third, a wider magnet head would have been nice. But for only $12, I'm willing to sacrifice some of the niceties of other, pricier tools. If you ever find yourself in need of something like this, I recommend this model, despite its shortcomings. I can see this tool being useful in other situations (i.e. when I drop a screw or nut in the garage and it rolls out of reach).

On Keeping Records

Mar 21, 2023

My wife and I are in the process of purchasing a new home. The current owners of the property we're buying have kept meticulous records. They have a spreadsheet detailing:

  • The type of home update
  • When the update was made
  • Who did the service (or where it was purchased from)

This information is surprisingly useful to know, and has helped us make some decisions on what to update and what not to update. It also gives us a good picture of the relative ages of each home system (e.g. hot water heater, HVAC units, etc.).

While discussing the selling of our current home with our realtor, I figured I could provide dates for when some of the similar updates here were done. I assumed that I had emails on these items, but it turns out I don't. Maybe I paid for some of these things via a check? And maybe the receipt they sent was a hard-copy? Regardless, I am unable to pinpoint when our hot water heater was installed, for example (this kind of thing is useful to know when selling a home, it turns out).

It's frustrating to know that I had work done to my home but cannot locate the invoice showing me who did it or when it was done. Had I kept better records, this wouldn't have been a problem. Going forward, I intend to do a better job.

The Future of AI

Feb 14, 2023

Today's Tom Scott video feels very prescient. I too have had the feeling that we are standing on the cusp of something big; though I'm not sure if that something is good or bad. The problem Tom was trying to solve was also interesting (who knew that subtle quirk about GMail's labels?!?).

My text editor of choice when I'm in Linux-land is Vim. One of its most powerful features is the ability to use regular expressions when searching. This capability makes hunting through giant log files so easy. However, I ran into a problem today that I wasn't sure how to work around.

I was looking through a large log file, trying to find the lines that included the text Internal Server Error. Making this difficult, however, were hundreds of such entries that were calling a known failing case. I wanted to weed out these known cases, and with regular expressions, I was able to. The known case I wanted to ignore looked something like the following:

[02/Feb/2023 16:06:14] ERROR [log.py:224] Internal Server Error: /api/v1/some_bad_endpoint/

This endpoint is the only one in the /api root, so I wanted to ignore /api as a part of my search. The magic is through negative look-ahead assertions, the syntax for which I was unfamiliar (\@! being the key). My search command in Vim ended up being:

/Internal Server Error: \/\(api\)\@!

Using this regular expression helped me jump to every instance of the Internal Server Error text that wasn't a call to the known-failing /api root. So handy!

I learned this morning that The New Yankee Workshop is now posting full episodes of the show to YouTube! The woodworker in me is freaking out just a little bit at this news; there are so many good episodes of this classic show! Below is a small message from Russell Morash, the producer of the show (and the owner of the shop where they shot the show). It's a neat walk down memory lane.

The story in this Essential Craftsman video is truly remarkable; it's well worth its 10 minutes.

Sublime Text Settings

Jan 17, 2023

I use Sublime Text as my primary code editor, and I use it in a number of places (my work laptop, a personal laptop, my home desktop computer, etc.). As a help to myself in keeping the environments synced, here are the settings I like using within Sublime Text. Having this kind of thing logged somewhere is such a help when setting up new environments.

Last updated: May 15, 2024

{
    "auto_complete_commit_on_tab": true,
    "always_show_minimap_viewport": true,
    "bold_folder_labels": true,
    "caret_extra_top": 2,
    "caret_extra_bottom": 2,
    "color_scheme": "Mariana.sublime-color-scheme",
    "draw_minimap_border": true,
    "ensure_newline_at_eof_on_save": true,
    "font_face": "Consolas",
    "font_size": 10,
    "hardware_acceleration": "opengl",
    "hide_new_tab_button": true,
    "highlight_line": true,
    "highlight_modified_tabs": true,
    "ignored_packages":
    [
        "Vintage",
    ],
    "indent_guide_options": ["draw_normal", "draw_active"],
    "indent_to_bracket": true,
    "index_files": true,
    "revert_font_size": 10,
    "rulers": [80, 100],
    "scroll_speed": 0,
    "show_encoding": true,
    "show_line_endings": true,
    "theme": "Adaptive.sublime-theme",
}

These are the key-bindings that I use:

[
    { "keys": ["ctrl+shift+v"], "command": "paste" },
    { "keys": ["ctrl+v"], "command": "paste_and_indent" },
    { "keys": ["ctrl+="], "command": "file_diff_menu" },
    { "keys": ["ctrl+shift+="], "command": "file_diff_previous" },
    { "keys": ["ctrl+alt+p"], "command": "prompt_select_workspace" },
    { "keys": ["ctrl+shift+'"], "command": "change_quotes" },
    { "keys": ["ctrl+,"], "command": "prev_modification" },
    { "keys": ["ctrl+0"], "command": "revert_font_size" },

    // LSP
    // Jump to next Diagnostic in Tab
    {
        "keys": ["ctrl+k", "n"],
        "command": "lsp_next_diagnostic",
        "context": [{"key": "setting.lsp_active"}]
    },
    // Jump to previous Diagnostic in Tab
    {
        "keys": ["ctrl+k", "p"],
        "command": "lsp_prev_diagnostic",
        "context": [{"key": "setting.lsp_active"}]
    },

    // Origami
    { "keys": ["ctrl+k", "shift+up"], "command": "create_pane", "args": {"direction": "up"} },
    { "keys": ["ctrl+k", "shift+right"], "command": "create_pane", "args": {"direction": "right"} },
    { "keys": ["ctrl+k", "shift+down"], "command": "create_pane", "args": {"direction": "down"} },
    { "keys": ["ctrl+k", "shift+left"], "command": "create_pane", "args": {"direction": "left"} },
    { "keys": ["ctrl+k", "ctrl+up"], "command": "carry_file_to_pane", "args": {"direction": "up"} },
    { "keys": ["ctrl+k", "ctrl+right"], "command": "carry_file_to_pane", "args": {"direction": "right"} },
    { "keys": ["ctrl+k", "ctrl+down"], "command": "carry_file_to_pane", "args": {"direction": "down"} },
    { "keys": ["ctrl+k", "ctrl+left"], "command": "carry_file_to_pane", "args": {"direction": "left"} },
    { "keys": ["ctrl+k", "ctrl+z"], "command": "zoom_pane", "args": {"fraction": 0.9} },
    { "keys": ["ctrl+k", "ctrl+shift+z"], "command": "unzoom_pane", "args": {} },
]

Here also is my Package Control configuration, which includes a list of the packages I have installed on my primary work development system (which is where I do most of my development work anyways):

{
    "bootstrapped": true,
    "in_process_packages":
    [
    ],
    "installed_packages":
    [
        "A File Icon",
        "BracketHighlighter",
        "Center Comment",
        "ChangeQuotes",
        "Django Syntax",
        "DotENV",
        "Emmet",
        "FileDiffs",
        "Indent XML",
        "LSP",
        "LSP-json",
        "LSP-ruff",
        "MarkdownPreview",
        "Origami",
        "Package Control",
        "PackageResourceViewer",
        "PlainTasks",
        "Pretty JSON",
        "RevertFontSize",
        "Side-by-Side Settings",
        "SublimeLinter",
        "Terminus",
        "TodoReview",
    ],
}

As I mentioned last month, my desktop computer at home bit the dust. Most of the components in that system heralded from the late twenty-oughts, making them beyond time to be replaced. I have since replaced all the old stuff with new stuff, and I'm impressed (though not surprised) at how far computer hardware has come since my last upgrade.

I finally upgraded my computer case, going with a Fractal Design Meshify 2 Compact model. This case is so incredibly quiet! Even under load, you can barely hear the fans spinning. In fact, this system is now quieter than the laptop I use for work, which is itself fairly silent. Another nice feature of this case is the cable routing options. Cable routing has clearly been given a lot of attention over the past decade or two; it's now very easy to keep your case neat and tidy.

The other major upgrade I made was in the power supply department, opting for a Corsair RMx Series (2021) 750W unit. Interestingly, the fan on this unit doesn't ever turn on at idle; only when it's under load does it spin up. The modular design also makes it easy to use only what cables you need, again keeping things neat and tidy.

I decided to bite the bullet and upgrade to Windows 11 with this build, and I've been pleased so far with the upgrade. As is usual with Windows, there are a number of quirks and annoyances, but that's always been the case. Some of the applications I use, specifically those built with the .NET framework (e.g. KeePass), now start instantly. It's nice to see that some effort has been put into little stuff like that.

All in all I'm pleased so far with the upgrade. I've had an intermittent lockup occur a couple of times, and I'm trying to track that down, but otherwise it's been incredibly smooth sailing.

Another recent video game I picked up on sale was Subnautica: Below Zero, a follow-up to the 2018 original. I really enjoyed the original; in fact, I still occasionally fire it up to play. This new entry, however, was only mildly enjoyable.

Crafting was a joy in this game, as it was in the original; there are tons of things to make, and you have to explore the world (scanning stuff in the process) to learn what's useful and what's not. The world is definitely much smaller than in the original, but the biomes are a little more diverse: they look vastly different from one another. Some of the biomes were fairly inventive as well (the floating lily pad islands being one memorable one). The story was mediocre, but what really bothered me was the lack of direction in the game. Most of the time you're not sure where to go or what to do. The original did a great job of nudging you in the right direction. In it, you would pick up distress calls from certain locations, each of which was usually near some point of interest that helped you progress.

In this title, however, there's none of that. Often, you just have to stumble upon the right place to move the story along. There are many maze-like areas of the world map, some of which were frustrating to navigate. This makes finding some of those key locations very difficult. All of the above-water locations were a nice new addition, but were equally confusing to navigate. This game either needs an in-game mapping function (maybe something that you have to craft), or it needs better nudges for the player. I eventually had to break down and look online for the location of a few key places; some of them are incredibly well hidden, which is odd given that they are key to moving the story forward.

I've already uninstalled this game (I don't envision playing it again), but it was fun while it lasted. I'll definitely check out any sequels that get made, if only because the first game was so memorable and fun. My verdict: C+.

Back in November, I picked up Doom Eternal on Steam during a weekend sale. It's a relatively old game now (released in March of 2020), but having kids means I don't game as much as I used to. I'm glad I picked it up on sale, because the game was a letdown. How this game got such stellar reviews is beyond me. Maybe it's a sign of the state of first person shooters these days?

As is expected from an id Software game, the game's graphics are stellar. Even on my old rig it looked great and played smoothly. That's about where my praise ends, however. Combat in this game is a chore. Ammo is super limited, which is clearly a design decision to force you to use the game's "glory kill" mechanic. Levels, though beautiful, are the now-typical arena-hallways-arena style design: fight enemies in an arena, then walk through some hallways (or do some jumping puzzles) to get to the next arena.

A few of the enemies were fun, with a number of callbacks to the original, but some of them were a real pain to deal with. I ended up having to lower the difficulty of the game just to get through it; some of the arena battles were just too much of a slog. The story is weak, though I didn't expect much in this department; all id Software games traditionally have terrible story lines.

All in all, I don't envision playing this game again. On my typical letter-grade scale, I'd give it a solid D (for disappointing).

Aging Hardware

Dec 15, 2022

The December round of updates from Microsoft has toasted my desktop computer. For some reason, the system enters a blue screen loop of death on boot. I've been able to restore to a previous system restore point, but now Chrome, Brave, and other applications don't work. I may try reinstalling Windows from scratch to see if I can limp along a little more, but it looks as though it's time for new hardware.

My current motherboard, CPU, memory, and primary SSD were all purchased in August of 2014; over 8 years ago! The graphics card I'm using is nearly 6 years old (purchased in January 2017). I'm not sure how old my power supply and case are; they're old enough that I can't find emails stating when I bought them. The case particularly needs to be replaced; it's fairly noisy (though, back in the day, it was a stellar case).

Upgrading computer hardware is generally a headache (there are too many options to wade through!), but the end result is worth the effort.