A New Garage Door

Sat, Feb 18, 2023 | life, and home ownership

Oh cool, I own a home, I can be a DIY-er (do-it-yourself-er)!

– me in ~2019

My garage gym took two years to build because I made poor choices.

My garage gym took two years to build because I made poor choices.

One of those poor choices was to insulate the walls of the garage. In the process, I took down the 50-year-old garage door opener and railing. But, when I finished the insulation project, I decided not to replace the opener and resorted to manual operation of the garage door.

Okay, I lied. I didn’t decide to have a manual garage door.

I attempted to mount the railing and the garage door opener. But, I stripped one of the railing’s lag bolts during the process. Only one lag bolt held the railing to the wall rather than two. When I opened the garage door after my DIY re-installation, the opener pulled the railing right off the garage wall, and both the railing and opener came crashing down.

Oops!

I ordered the garage door through Costco, which earned me a nice Costco gift certificate worth 15% of the cost of the new garage door. Sadly, I can burn through this gift certificate in two trips.

The new garage door took three months from order to installation. The salesman said the delay was due to freight delays from the pandemic and China’s COVID Zero policy, where large portions of citizens were quarantined for weeks.

That was three months ago. The new garage door was installed a few weeks ago, and I love it. I never thought a garage door could change how much I liked being in the garage.

Front view

The configuration I purchased included side-mounted garage door opener, which made the garage feel spacious.

Front view

I Poo You

Thu, Feb 16, 2023 | potty-training, kiddos, and life

My wife said, “Pax said he pooped a baba poop, a ti ti poop, and a paxie poop.”

Pax calls me baba, Sky ti ti, and himself Paxie. I don’t know why he gives himself a longer nickname than his name, but I digress.

I was in the middle of putting the leftovers from lunch into the fridge, so I didn’t quite comprehend the statement.

I said, “Huh?”

My wife said, “That’s what he pooped.”

Looking towards Pax, I said, “What did you poop, Pax?”

He screamed, “I POO YOU BABA!!!!”

Yup. Okay. Thanks, my dude.

Cycle Review: I'm Back

Sun, Feb 12, 2023 | lifting, and cycle review

The Jan 2 to Feb 12 cycle is now complete! This is the first lifting cycle in my new home gym. And it is also the first cycle in nearly three years since the pandemic shut down everything.

Three years!

I programmed six days a week and allowed one rest day:

  • Mondays: back squat
  • Tuesday: press and pull up negatives
  • Wednesday: kettlebell conditioning
  • Thursday: power cleans and single-leg squats
  • Friday: ring rows and push-ups.
  • Saturday: Accessories like handstand holds, tibialis raises, dip negatives, and hip flexors.
  • Sunday: Rest Day.

The rest day was actually flexible because life is like a bee: busy.

Overall, I didn’t feel the workout was too much or too little. Most sessions I could finish in forty minutes or less – including warm-ups. But, I felt the pump and it never felt easy.

This past week, I performed a lot of heavy singles to gauge how strong I became. The results are in: I’m weak.

I need to work on trunk stability. In both the back squat and power-clean heavy singles, my legs felt fine strong. But, my upper body was weak. I had to stop my heavy single attempts because my upper body could not brace heavier weight.

The last thing I’ll say about the cycle was how surprised I was at falling back into the habit of lifting. The first day was a back squat, and the feeling of the empty bar made me feel like I was in my old gym again. It’s like when you hear a song from your past, and you remember what you were doing or watching when you listened to the song.

But it wasn’t only the feeling of stepping into the gym/garage. The nights before the next workout, I checked my program and mentally prepared myself by walking through the lifts. For the days with heavy lifts scheduled, I turned down earlier, ensuring at least eight hours of rest. When I worked out early in the mornings on days I had to commute to the office, I ate extra carbs the night before. For workouts at the end of the day, I made sure to raise my desk so I would stand at work to help loosen up my body.

Even though I hadn’t been to the gym in three years, I felt like I hadn’t left.

On to the next cycle!

Add One Kilo

Fri, Feb 10, 2023 | lifting

It was a hot summer day in Oakland. I stood on the lifting platform, readying my mind for the next clean.

I had been working my way through a six-week cycle that required attempting 15 cleans every 60 seconds. We called them OTMs or “on the minutes.”

At the start of the cycle, I started at 65% of my personal record. If I attempted all 15 cleans successfully during the OTMs, then I was allowed to attempt a heavy single for the day.

And today was the last week of the cycle. I had completed my 15 cleans and worked my way up to a 104kg clean for my heavy single. 104 didn’t feel heavy, and I was feeling strong, which doesn’t happen often.

I was also two kilograms away from my personal record – 106kg.

I started loading weights for the next clean to match 106kg.

My coach saw what I was doing and said, “Add one kilo! There’s no point with 106 because you already did it.”

Up to this point, I had never thought about strategizing to beat my PRs. I did most of my lifting previously in a CrossFit gym where I had, at best, 15-20 minutes to perform cleans before the coach moved us onto the met-con workout. I would warm up, complete three to five sets of the lift, unload the bar, and move on quickly.

This compared to the OTM workout, where 15 minutes was only the beginning of the training, it was no wonder I never thought about beating PRs during CrossFit classes.

Either case, my coach knew I previously cleaned 106kg – it was on the whiteboard where my teammate’s PRs were displayed. He saw no reason to attempt the same weight since I had proven it before.

He explained that in the best case, I would successfully lift 106. But that’s nothing special since I’ve done it before. Additionally, 106 would leave me without enough energy for another attempt – remember, it’s a maximum-effort attempt.

In the worst case, I fail at 106, and I’m demoralized knowing I couldn’t lift a weight I had previously lifted.

With 107, the best case is I have a new PR – the best feeling. The worst case is I leave knowing I tried my best to beat my previous PR – I could hold my head up high.

So, I added one more kilo to the bar.

I sucked in a belly full of air, pulled the bar off the ground, and exploded upwards; at the top of my extension, I immediately pulled myself under the bar. As it came down, the bar crashed slightly on my shoulders, but I caught it in the squat. Instantly, I bounced up from the bottom, grinding my way to stand. At the top, I dropped the bar and exhaled loudly.

I walked to the whiteboard, crossed the six from “106kg” and replaced it with a seven.

Python Debugger

Wed, Feb 8, 2023 | python, and programming

I came across an interview with John Carmack where he talked about debuggers, a tool near and dear to my heart. Here are a choice few quotes from Carmack:

It still boggles my mind how hostile to debuggers and IDEs that so much of the big money-get-billions-of-dollars venture backed companies are.

A debugger is how you get a view into a system that it so complicated to understand. Anybody that thinks “just read the code and think about” – that’s an insane statement. You can’t even read all the code on a big system.

While I agree with his statements, I found them strange.

I used debuggers since my early days programming in ruby. One of the first things I did when I joined my current company was figure out how to get the python debugger to run in the development environment. The debugger was a natural extension to the REPL that it made so much sense. Everyone I worked with used the debugger, so I didn’t quite understand why the big companies weren’t using it as much.

Maybe it’s the fact that they don’t use IDE-based debuggers, for which I have no experience with.

Either case, I love debuggers. And since I’ve been learning python, let’s see how the python debugger works.

Python’s debugger is apart of the standard library: pdb. It works well in a pinch, but I find it primitive.

Instead, I rely on ipdb. ipdb has the same interface, but enables tab completion and syntax highlighting; I’m all for looking at pretty terminals.

Let’s set it up.

Install ipdb

To install ipdb, we use pip:

pip install ipdb

Setting Breakpoints

When you need to invoke the debugger, add the following line to where you want to debug code.

# pdb
import pdb; pdb.set_trace()

# ipdb
import ipdb; ipdb.set_trace()

If you find this verbose like I do, and you’re using python 3.7+, then PEP 553 has a nice addition for you: the breakpoint() statement.

breakpoint removes the need to import the debugger package and call set_trace().

Additionally, breakpoint is a single statement rather than two separated by a semi-colon – ensuring black leaves my line alone when I save the file.

And finally, PEP 553 allows configuring which debugger to use with the built-in breakpoint statement. By default, it uses pdb from the standard library. But, you can use ipdb by setting the PYTHONBREAKPOINT environment variable:

PYTHONBREAKPOINT="ipdb.set_trace" python myscript.py

For the rest of this post, I use breakpoint.

Line by Line

Let’s walk through the following python script.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# filename: debugger.py


def say(msg: str) -> None:
    print({msg})


def main() -> None:
    breakpoint()
    print("I'm inside main.")
    prefix = "Loop:"
    for i in range(5):
        say(f"{prefix} {i}")

    print("I'm exiting")


if __name__ == "__main__":
    main()

From the terminal, execute the script:

PYTHONBREAKPOINT="ipdb.set_trace" python debugger.py

The execution should output our source code with line numbers, an arrow, and a prompt.

Welcome to the debugger!

1
2
3
4
5
6
> /Users/peter/code/python-debugger/debugger.py(10)main()
      9     breakpoint()
---> 10     print("I'm inside main.")
     11     prefix = "Loop:"

ipdb>

The execution stopped at line 10.

Let’s break down the output.

  • Line 1: Shows the file the debugger is paused in, the line it stopped on (10), and the function name it is paused in.
  • Line 2-4: Shows the line number in the file (not the output) and the code on that line. On Line 3, the debugger placed an arrow (--->) indicating the next line to execute.
  • Line 6: The debugger prompt. This is where you enter commands.

Now, type “next” and press Enter to submit the command.

The terminal displays:

1
2
3
4
5
6
7
8
>ipdb> next
I'm inside main.
> /Users/peter/code/python-debugger/debugger.py(11)main()
     10     print("I'm inside main.")
---> 11     prefix = "Loop:"
     12     for i in range(10):

ipdb>

The debugger executed the code print("I'm inside main.") and printed I'm inside main. to the terminal. This is shown on line 1 of the output above. The debugger now points to line 11, the next statement in the program. Finally, the debugger is now waiting for you to submit the next command.

So what happened?

The next command continues the program’s execution until it reaches the next line. We’ll use this command often to move through the debugger. Luckily, there’s also a shortcut for next: the letter n.

Actually, many of the commands in this post have shorter forms. I’ll stick with the long form, but you can find the short versions in pdb’s documentation.

Back to the execution.

The program has executed line 10 and stopped. The next line to execute is line 11 which creates a variable.

Type “next” and press Enter.

The output now displays:

ipdb> next
> /Users/peter/code/python-debugger/debugger.py(12)main()
     11     prefix = "Loop:"
  -> 12     for i in range(5):
     13         say(f"{prefix} {i}")

ipdb>

Now we’re ready to look into inspection.

What’s that variable set to?

The program executed line 11 and now the variable prefix exists. Let’s find out what it is – forget the fact that you know what the source code is for a moment.

We can inspect prefix with several commands.

Type “p prefix” and press Enter.

The output now displays:

ipdb> p prefix
'Loop:'
ipdb>

The p command evaluates the expression provided and prints the result. In our case, we evaluated the prefix variable and printed the value: 'Loop:'.

There is related command, pp that does the same thing as p, but uses pprint to print the output. I like to use this when printing nested dicts or lists.

Before we continue, let’s type in one more inspection command.

Type “whatis prefix” and press Enter.

The output now displays:

(Pdb) whatis prefix
<class 'str'>
(Pdb)

The command whatis prints the type of the expression, in this case the type of the variable prefix. whatis effectively executes the following:

print(type(prefix))

Hold on, where am I?

Before we move on, let’s refresh ourselves with where the python executor is paused.

Type “list” and press Enter.

You the output now displays:

(Pdb) list
  7
  8  	def main() -> None:
  9  	    breakpoint()
 10  	    print("I'm inside main.")
 11  	    prefix = "Loop:"
 12  ->	    for i in range(5):
 13  	        say(f"{prefix} {i}")
 14
 15  	    print("I'm exiting")
 16
 17
(Pdb)

The list command shows the familar -> arrow where the execution is paused. In addition, list shows the five lines above and five lines below the ->.

If you typed list and pressed Enter again, list shows the next set of lines. You can repeat the command until the end of the file.

If you decided to list the rest of the file, then to show the current line again, you can type “list 11”.

The related command longlist, when submitted, shows the code for the current function you’re paused in.

Type “longlist” and press Enter.

The output now displays:

(Pdb) longlist
  8  	def main() -> None:
  9  	    breakpoint()
 10  	    print("I'm inside main.")
 11  	    prefix = "Loop:"
 12  	    for i in range(5):
 13  ->	        say(f"{prefix} {i}")
 14
 15  	    print("I'm exiting")
(Pdb)

Yup, that’s the full definition for our main function.

Up and Down the Stack

Let’s return to moving around. We are currently paused right before the say() function call inside our for loop. Let’s learn how to step into functions.

Type “step” and press Enter.

The output now displays:

(Pdb) step
--Call--
> /Users/peter/code/python-debugger/debugger.py(4)say()
-> def say(msg: str) -> None:
(Pdb)

Execution has now paused at the definition of say(). And at this location, we can print out the arguments by using our inspection commands. In otherwords, the execution has moved into say(), before executing the body of the function. At this location, the arguments are available for inspection.

Since we covered inspection, let’s move to the next line and list the source code.

Type “next” and press Enter. Type “list” and press Enter.

The output now displays:

(Pdb) next
> /Users/peter/code/python-debugger/debugger.py(5)say()
-> print({msg})
(Pdb) list
  1  	# filename: debugger.py
  2
  3
  4  	def say(msg: str) -> None:
  5  ->	    print({msg})
  6
  7
  8  	def main() -> None:
  9  	    breakpoint()
 10  	    print("I'm inside main.")
 11  	    prefix = "Loop:"
(Pdb)

Now let’s learn about moving around the call stack since the execution is paused in say().

Type “where” and press Enter.

The output now displays:

(Pdb) where
  /Users/peter/code/python-debugger/debugger.py(19)<module>()
-> main()
  /Users/peter/code/python-debugger/debugger.py(13)main()
-> say(f"{prefix} {i}")
> /Users/peter/code/python-debugger/debugger.py(5)say()
-> print({msg})
(Pdb)

The where command prints the stacktrace where the execution is paused. The stacktrace is printed sequentially with the entrypoint of the script at the top and the location we’re paused at at the bottom. This output displays exactly where we are in the stack trace and provides an anchor for the next two commands: up and down.

Let’s move up to main().

Type “up” and press Enter.

The output is now:

(Pdb) up
> /Users/peter/code/python-debugger/debugger.py(13)main()
-> say(f"{prefix} {i}")
(Pdb)

We are now back at the callsite for say() within our main() function. To move back down, well …

Type “down” and press Enter.

The output is now:

(Pdb) down
> /Users/peter/code/python-debugger/debugger.py(5)say()
-> print({msg})
(Pdb)

Now we’re back.

I like to use up and down to peek into a function callstack as I debug code. They also come in handy when looking into open source libraries!

Let’s continue moving through the say() function and see what happens when it returns.

Type “next” and press Enter.

The output is now:

1
2
3
4
5
6
(Pdb) next
{'Loop: 0'}
--Return--
> /Users/peter/code/python-debugger/debugger.py(5)say()->None
-> print({msg})
(Pdb)

The output here looks intense. Let’s review line by line.

  • Line 1: The command we typed.
  • Line 2: The output from executing the print() function
  • Line 3: Indicates that the function has completed and is returning.
  • Line 4: Shows the filepath, linenumber, function and the return value (None).
  • Line 5: The last line that was executed before the return. Since the execution has actually moved back to the upper stack, the debugger repeats the line to let you see the return value.

Now that execution has paused after returning, we can move forward to the next line and expect to pause at the for loop in main().

Type “next” and press Enter:

The output is now:

(Pdb) next
> /Users/peter/code/python-debugger/debugger.py(12)main()
-> for i in range(5):
(Pdb)

Indeed, we are back in main().

Finishing Up

Since the rest of the program is finishing up the loop and another print statement, let’s finish up.

Type “continue” and press Enter.

The output is now:

(Pdb) continue
{'Loop: 1'}
{'Loop: 2'}
{'Loop: 3'}
{'Loop: 4'}
I'm exiting
(venv) peter@doomslug ~/code/python-debugger %

The continue command resumes the execution. If there are no other breakpoint() function calls in the code path, the execution completes and exits successfully like above.

And we’re done!

There are times when continue isn’t exactly what you want to do. For instance, maybe you found the bug you were looking for and you want to quit.

Luckily, there’s the quit command. It terminates the execution and returns you to the shell.

Help!

One last tip. If you’re in the debugger and forget what the commands do, the help command is handy. Without any arguments, it lists all the available commands. With a command name as the argument, help prints out the documentation for the command.

Until Next Time

With next, step, p, pp, list, longlist, where, up, and down commands, you’ll have plenty to use in your next debugging session.

Enjoy!

Seasonal Best

Sun, Jan 29, 2023 | lifting

I racked 145kg loaded barbell into the squat rack. I bent down slightly and stepped away from the squat rack. Steve, my coach, yells his signature, “WOOOOOO!”

I was feeling a measured level of excitement. This is the heaviest back squat I’ve done in a year. But this wasn’t the heaviest I’ve ever squatted. A year ago, I squatted 148kg.

My friend on the platform next to me congratulated me on the lift saying it was my new seasonal best.

I have never heard of that term before.

A seasonal best (SB) is the best successful lift you’ve completed based on the past few cycles.

“It’s hard to keep increasing in PRs year after year, especially with setbacks like injuries. So, I use SBs to keep my goals smaller” he said.

I liked this perspective.

Home Gym

Sun, Jan 22, 2023 | gym, and lifting

As I punctured the garage wall with my hammer, I realized how difficult it was to demolish drywall. I stuck my hand in the hole to grab hold of the drywall. I yanked on the drywall, losing my grip and nearly falling. Funny, demolishing a wall shouldn’t be this hard, should it? I imagined that there were machines to do this.

At the same time, it dawned on me that the demolition goal was to insulate the garage walls so that I could drop loaded barbells without my neighbors calling the police.

Hopefully, the neighbors weren’t reaching for their phones as I swung my hammer through another part of the wall.

Anyways, I built a gym.

I finished it last November over Thanksgiving break. It took nearly two years to build.

Front view Side view

The platform is 7'36" x 10'. I started with 8' x 10'. But, at 8 feet long, the platform ends about four inches into the space in front of the doorway to the house. The black underlayment in the picture above is where the platform finished initially. But, at eight feet, there’s enough of a step that a kiddo or wifey could easily roll their ankle when they stepped in the garage. So I cut it down.

The platform is made of four layers. The bottom layer is black rubber underlayment about a quarter of an inch thick. Initially, I thought the underlayment would help with sound dampening. But actually, it provides a slightly more even surface to lay down the platform.

On top of the underlayment is a wedge.

The wedge is made of layered 2.7mm plywood. It’s designed to level the sloped floor in my garage.

Wedge top view

Wedge side view

The wedge idea was taken from this youtube video.

On top of the wedge is a layer of 19/32 inch OSB board.

Yes, 19/32 inches.

Not 1/2 inch. Not 3/4 inch. But, 19/32 inch.

Why is 19/32 a standard measurement for OSB board? I have no idea but it was light enough to load by myself after being nearly pinned by a 3/4" maple plywood.

In either case, the OSB is what I screw the top layer onto. It also provides a bit more stability when I’m dropping weights on the platform.

As mentioned, on top of the OSB is a 3/4" maple plywood. On both sides of the platform is 3/4" rubber flooring from rubberflooringinc.com.

The rest of the equipment is as follows:

I’m a big Rogue fan. My old gym had a lot of rogue gear that held up to any torture CrossFitters could throw. I also love that most of their products are made in the USA.

The wall-mounted rack enables using the full size of the platform for olympic weightliftng. Well, it would if I had mounted the rack high enough.

If you look at the bottom of the wall where the rack is mounted, there’s a concrete ledge. The ledge comes off the wall about three inches.

When I measured how high to mount the rack on the wall, I tried to add enough height so that when I folded the rack back into the wall, the feet of the rack would sit on top of the concrete wall.

Unfortunately, I miscalculated.

So now the rack sits slightly angled away from the wall when I fold it back.

I bought concrete chisels to carve space for the racks, but I still need to get around to chipping away.

One last thing I’ll mention about the rack is that it should be installed with two people. Those black slingers are heavy.

The Vertical Plate Tree was not in the original plan when I was building out the gym. I originally wanted a wooden bench where I could store the weights underneath like in the bottom right of this Catalyst Athletics video. But the desire to finish and start working out was stronger than buying wood to build the platform.

That said, I’ve been working out for nearly three weeks and I love it.


Oh, and the demolition of the garage walls? I hired a guy.

So you know how pathetic it was for me, I spent an hour knocking out a 2’x4' hole in the wall.

In the same time, the guy demo’d half the walls in my garage by himself. His friend showed up shortly afterward, and finished the other half of the garage. In total, they worked three hours.

I paid the gentlemen and walked into the house slightly shorter than normal.

A Broken Cookie

Sun, Jan 8, 2023 | potty-training, kiddos, and life

I walked up to Pax sitting on his potty. He missed the order of operations of again. His urine-soaked pants were at his ankles. The potty was dry.

I asked him to stand up and I helped him take off his pants.

Pantless, Pax ran to his toy truck near by and rolled it around the floor while blowing raspberries as makeshift engine noises.

I took Pax’s pants and threw them in a growing pile of his wet clothes. I grabbed a pair of pants from the dry pile next to the wet pile.

I hope these pants were dry because they were clean and not because the wet pile became the dry pile.

I made a mental note, likely to be forgotten immediately, to rearrange these piles further apart after this parental task.

I walked towards the living room where Pax was pushing his truck around on the couch and I noticed a broken cookie with crumbs on the ground.

The kids must have stepped on it.

I put Pax’s pants down, picked up our handheld vacuum and walked to the broken cookie.

Wait. Did I give him a cookie?

I stopped the vacuum’s nozel right before reaching the cookie.

I noticed the cookie was moist.

We don’t have cookies like this in the pantry, do we?

There was the smell fart in the air.

Ah, shit, that’s not a cookie. Sigh.