Sunday, September 6, 2020

You reap what you sow: Lottery and lightening

"You reap what you sow"

My mom loves quotes. Growing up, I heard this one a lot.

It always came after someone's downfall, as if they should've known better. As if the universe is governed by this simple phrase and life can be mastered by living within its bounds.


But, then, why doesn't life ever seem to go the way it's supposed to?


Consequences, good and bad

We all learn that talking behind someone's back leads to conflict.

We all learn that practicing leads to better performance.

In other words, the actions we take today produce the consequences we experience tomorrow.


But, I still didn't pass the job interview I spent hours and hours preparing for. The one I have over 10 years of highly relevant experience for.

And while I was failing my interview, the guy who was out partying when I was studying, the guy who was spending lavishly while I saved money for retirement.. that guy just made a million dollars trading options in the stock market.


Why isn't the universe operating like it's supposed to?


Why am I such a loser that even the universe bends its rules to show me how worthless I am?


There's fundamental truth in the statement that our actions have consequences. It's easy to understand. It's easy to observe.

It gives us an easy way to make sense of the things we experience.


And it makes it easy to overlook a harmful consequence of internalizing this framework.


If we see everything as a result of our actions, it becomes reeeally easy to blame ourselves for anything and everything that happens to us.

That interview I didn't pass? I should have studied hard. Oh, I did study hard? Well.. I don't know. I guess I'm just stupid.


If we see everything as a result of our actions, it's easy to miss the fuller picture and all the variables at play. It's easy to overlook and under-appreciate how much is outside our control.


Want to win the lottery?

Have you ever dreamed of winning the lottery?

Sure you have.. a song like Billionaire doesn't go 4-times platinum without a lot of people resonating with it's hook, "I wanna be a billionaire, so fucking bad".


Either way, the universe doesn't care.


We both know no one wins the lottery through sheer force of desire, no matter how much they want it.


The trick to wining the lottery's simple: Buy a ticket

The single pre-requisite for winning the lottery is buying a lottery ticket.


Here's a fun fact Your chance of winning the lottery is 1 in 14 million.


The trick to getting struck by lightening's simple: Stand outside in a thunderstorm


Here's another fun fact: Your chance of being struck by lightening in any given year is 1 in 500 thousand.


Wining the lottery's a pretty positive consequence of buying a lottery ticket.

Getting struck by lightening's a pretty horrible consequence of standing outside during a thunderstorm.


In both situations, we have some control over the thing we do or don't want to happen. But, we have very little control over it.


Sometimes, shit happens

The point of all this is that sometimes, shitty things happen to us. A lot of shitty things've happened to me, and I'm sure a lot of shitty things've happened to you.

I've also been fortunate enough to have had a lot of good things happen to me too.


These days, I'm less inclined towards attributing everything that happens in my life to my actions. I'm more inclined towards doing what I can to make good things happen, and recognizing how much outside my control contributes to all things that happen.


Takeaway: It's not your fault, but it is your responsibility

It's unhealthy to blame to ourselves (or to accept blame from others!) when things outside our control lead bad things happening.

It's healthy to accept responsibility for acting on things within our control to make good things happen.


Easy to say, difficult to internalize.


But, I'm optimistic that it can happen with practice, and practicing is something within my control. ;)


--

Genevieve, thank you for reading a draft of this and discussing the topic with me in so much depth!


Friday, August 14, 2020

Dynamically loading modules in Python 3

Being able to programatically load python files as modules is a pretty cool capability of Python.

I'll jump to the punch, then step back to fill in context.

First, here's the file that dynamically loads peer python files as modules:

# File: dynamically_loaded_modules/__init__.py

import glob
import importlib.util
import sys

from os import path


def _get_module_name_from_file_path(file_path, module_name_prefix=''):
    if not file_path.endswith('.py'):
        raise ValueError(f"File doesn't have a '.py' extension: {file_path}")

    file_name = path.basename(file_path)
    file_name_without_ext = file_name[:-3]

    if module_name_prefix and not module_name_prefix.endswith('.'):
      module_name_prefix += '.'
    return module_name_prefix + file_name_without_ext


def load_modules(registry):
    path_string = path.join(path.dirname(__file__), '*.py')
    module_paths = [
        file_name for file_name in glob.glob(path_string)
        if not file_name.endswith('__init__.py')
    ]

    for module_path in module_paths:
        module_name = _get_module_name_from_file_path(module_path)

        if module_name not in sys.modules:
            module_spec = importlib.util.spec_from_file_location(module_name, module_path)
            sys.modules[module_name] = importlib.util.module_from_spec(module_spec)
            module_spec.loader.exec_module(sys.modules[module_name])

        module = sys.modules[module_name]
        if not hasattr(module, 'load_module') or not callable(module.load_module):
            raise Exception(f'Auto-load module {module} is missing required function "load_module"')

        module.load_module(registry)


Next, here's an example of a peer python file that gets dynamically loaded as a module:

# File: dynamically_loaded_modules/example.py

def load_module(registry):
    registry.register(f'{__file__} was dynamically loaded!')


Finally, here's example usage:

#!/usr/bin/env python

# File: app.py

from dynamically_loaded_modules import load_modules


class Registry:
    def __init__(self):
        self._registrations = []

    def register(self, description):
        self._registrations.append(description)

    @property
    def registrations(self):
        return self._registrations


registry = Registry()
load_modules(registry)
print(registry.registrations)


The use case I had for this was dynamically registering API routes for a flask app.

This solution offers the conenvince of registering new API routes via Flask's route. That's achieved by (1) passing the Flask app to load_modules and (2) calling flask_app.route in each dynamically loaded file's load_module function.

Actually, I take back what I said earlier. I'm not going to fill in too much context right now. Otherwise, I'd never publish this. :)

Instead, I'll try to come back later and update this post to break down the code more.

Thursday, February 6, 2020

How to Run an Old Version of macOS After Upgrading to Catalina

I have a MacBook Pro, which I'm usually happy with.

However, I recently wanted to play a game that's only available on Windows. So, I went about figuring out how to run Windows software on a Mac.

After some initial investigation, I had the following options:
  1. Run Windows in a virtual machine using software like VirtualBox or Parallels
  2. Install and run Windows on my laptop using Apple's Boot Camp
  3. Use a virtual Windows desktop using a product like Amazon WorkSpaces
  4. Use a compatibility layer to run the Windows application on my laptop without a Windows install using Wine or Crossover
I decided to dig deeper into using Wine because I didn't want to buy a Windows license or make recurring payments to Amazon.

Unfortunately, macOS Catalina (which is what I have) doesn't support 32-bit applications and the game I wanted to play requires 32-bit support. So, to run the game using Wine, I'd need to use an older version of macOS.

I wasn't willing to downgrade my OS for this, so I started looking into how to run multiple version of macOS on my laptop. Here's what I found:

Overview


To run a different version of macOS on the same laptop, we'll:
  1. Create a disk partition to hold a "bootable installer"
  2. Create a bootable installer for the older macOS version on that partition
  3. Create a disk volume to install the OS on
  4. Install the OS on that volume
WARNING: Creating, modifying, and erasing partitions can have serious consequences. You're at your own risk if you decide to attempt this.

Step 1: Create a disk partition


To create a bootable installer, we need somewhere to put it. A good option is a thumb drive. I didn't have one on hand, so I created a disk partition on my mac.

Basically, I followed Apple's instructions the instructions for creating a physical disk partition. The following are important:
  1. About 8GB of capacity is needed
  2. The partition must be formatted as "Mac OS Extended"
Here's how I set up my partition:
  1. Open Disk Utility and select "Show All Devices".
  2. Select the physical hard drive in the left panel and click the "Partition" button on the top.
  3. Select an existing partition in the pie chart, from which the new partition will be created (the existing partition will "donate" disk space for the new partition). Then, click the "+" button.
  4. Give your partition a name (the name is arbitrary - just use something you'll remember), select "Mac OS Extended (Journaled)" as the format, and set the size to 10 GB.
  5. Click "Apply" and be patient while the new partition is created.

Step 2: Create a bootable installer


For this step, I followed Apple's instructions for creating a bootable installer.

For me, this involved:
  1. Download macOS Mojave.
  2. Quit the installer (it started automatically after download, but we need to run it from the command line).
  3. Open a Terminal window.
  4. Run createinstallmedia on the new disk partition created in step 1:
    sudo /Applications/Install\ macOS\ Mojave.app/Contents/Resources/createinstallmedia --volume /Volumes/Bootable\ Install\ Partition.
    Note that your paths might differ. The second one depends on what you named your partition.
  5. Follow the prompts. Note that after this completes, the partition is renamed to something like "Install macOS Mojave".

Step 3: Create a new volume


We need to create a new volume on which to install the OS.
  1. Open Disk Utility again.
  2. Select the volume where macOS Catalina is installed. For me, this is "Macintosh HD".
  3. Click the "+" above "Volume" at the top.
  4. Enter a name for the new volume (I used "Mac OS Mojave"), leave the format as APFS, and click "Add".
  5. Wait while your new volume is created.

Step 4: Install the OS


Now, we just use the bootable installer from step 2 to install the OS on the new volume from step 3.
  1. Restart the laptop while holding down the option key. For additional guidance, see the "Use Startup Manager" section of Apple's guide for selecting a different startup disk.
  2. Once the laptop restarts, select disk with the bootable installer. For me, it was the one named "Install macOS Mojave".
  3. Begin installation by selecting Install macOS.
  4. Go through the install process. When prompted to select a partition to install the OS on, select the one created in step 3.

Wrap-up


That's it! After installation, your computer should startup in the OS you installed.

If you want to get back to your main OS installation, just follow the same procedure for using the startup manager (restart while holding the option key) and select it (for me, it's called "Macintosh HD").

So, after all this, I was able to run my Windows game on my Mac laptop by booting into the macOS Mojave install and using Wine.

All this so I could play a stupid game.. 😂🤦

I hope this helps with whatever you need it for!

Thursday, January 10, 2019

Traveling in Thailand, Day 2: First sights of the City

After a long day of travel, I slept a bit and woke up mid-morning. I didn’t have plans for my first day in Bangkok and since it was still Sunday in Seattle, I had plenty of time before needing to work. I decided to go visit the Grand Palace.

The palace grounds were stunning. The temples there are incredibly intricate and ornate, with metal, glass, and ceramic tiles.

My favorite aspect of the Grand Palace was the inner facade of the exterior wall. The entire thing's a mural. As you walk along it, you can feel an animated story unfolding. My curiosity led me to later learn this art captures the Ramakien, which is Thailand's national epic. I was surprised to learn that much of Thailand’s mythology was derived from Hindu mythology. When I get home, I'll be ordering an English translation.

After a couple hours of being captivated by these new sights, I wondered away from the palace and found myself in a market dedicated to trading in Buddhist charms. As I explored, I noticed a number of people intensely inspecting charms under high-powered magnifying glasses. It turns out there's a whole economy around Buddhist charms in Thailand. It's like a collectible economy where certain charms are believed to bring good luck. Some are rare because of they're one-off creations intended as gifts at royal ceremonies.

After walking around in the heat all day, I took a nap and woke up in the evening. I headed straight out to the premier Muay Thai stadium in Thailand, Rajadamnern Stadium. It was a thrill to watch such skilled athletes and I'd highly recommend it. It's also worth spending some time learning about Muay Thai in advance, because you'll get a lot more out of it.

Before heading home, I ended up making a final stop at a well-known Phad Thai restaurant called Phad Thai Thipsamai. I had the good fortune of choosing an Airbnb directly across the street. I don't actually like Phad Thai much because in the US, it tends to taste like noodles in ketchup. However, when I tried both their “modern” and “traditional” Phad Thai, I was surprised to find that I enjoyed the traditional one quite a bit. It seemed a bit less sweet, a bit more savory, and overall lighter on the sauce.

And, with the sun having risen in Seattle, I began my workday at 11:30pm in Bangkok.

Sunday, November 18, 2018

Traveling in Thailand, Day 1: Flying to Thailand

Today was a long day, but weeks of preparation definitely paid off.

I headed to the airport at 10:30am (thanks Maia!) and was feeling nervous on the way.

Did I forget something?
Will the car get a flat tire? Will I miss my flight?
What if I get confused or lost in the Beijing airport? What about in Bangkok?

Regardless of my slight anxieties, I got to the SEATAC airport in Seattle without issue and embarked on the longest day of travel I've ever had. About 10.5 hours to Beijing, 3.5 hours layover, then 6.5 hours to Bangkok.

Beijing was surprisingly smooth and easy to navigate. When I got off the plane, I just followed signs (that included English) for international transfers. They did a quick security check, which included the most thorough pat-down of my life (like, awkward-to-make-eye-contact-after level thoroughness), and I was on my way to the next gate. Luggage was transferred automatically, so I didn't have to pick up my checked bag, though I'm not sure that applies to all cases.

There isn't really much to do in the area of the airport where my flight to Bangkok was, so I just sat and waited. Since I use Google Fi, I was able to use my phone automatically - no local SIM card necessary. So, I checked in with friends and family.

After landing in Bangkok (BKK), I headed directly to immigration, per recommendations I'd read online. Contrary to what I'd read, there was virtually no line, but that might be because I arrived a bit after midnight. Without a single word exchanged, the immigration officer took my passport and papers, gave me my visa stamp, and I was on my way in about 3 minutes.

The luggage pick-up area is right behind immigration, Because I'd gotten through immigration so quickly, I had to wait a bit for my bag. More nervousness, of course, about whether it successfully made the plane transfer. But, it had.

Customs was a breeze because I brought so little with me and had nothing to declare.

I'd read a decent amount in advance about currency exchange. Here's what I found in-person. There're a ton of currency exchange kiosks in the baggage claim area, including teller desks and ATMs. There're also a ton outside and they're all the same company, so it doesn't really matter where you exchange money. My ATM card happens to reimburse any ATM fees, so I went ahead and used the ATM to get cash so I'd get a better exchange rate. I'd read recommendations to make sure to have smaller bills for taxis, so I tried to request an odd withdrawal amount (4900 THB instead of 5000), but it didn't go through. After switching to just requesting 5000 THB, it worked fine. For the smaller bills, I just asked asked at the exchange desk, and they were happy to change a larger bill for smaller ones.

After customs and exchanging currency, I headed down one floor and outside to the taxi area (there're plenty of signs, again all with English). There, I learned that you have to go to a line in front of taxi kiosks. You push a button on the machine and get a ticket that tells you which taxi stall to go to.

Now, taking taxis is something I'm really glad I read up on before arriving. My taxi driver tried every trick I'd read about.

Before getting in the car, I asked if the meter works. He confirmed this was the case. He turned it on when I got in the care, but seconds later, I noticed he'd turned it off. I asked about that immediately and he said he'd do a fixed price of 500 THB to my destination in the city. Although a flat price sounded appealing, I declined, feeling that the meter would be more fair, even if I had to pay more. So, he turned the meter back on at that point.

He indicated in a mix a Thai and a few words in English (I don't speak Thai, aside from a few words picked up from studying in the last couple weeks) he wasn't certain how to get to the destination. I don't think this was a deliberate effort to charge more (e.g. by taking a longer route), but I went ahead and pulled up a map on Google maps to provide a route. This might have made things easier for both of us.

At toll booths along the way, which passengers pay, he wanted me to use my smaller bills. I was reluctant as I was sure he'd later say he doesn't have change, so I said I prefer to use the larger ones. This gave me additional change for paying a more exact price.

Once we got to my Airbnb, the total came to 265 THB, a far cry from the original 500 THB he wanted to charge. As a courtesy, I rounded up to 300 THB, which seemed more than typical as I'd read in numerous places it's common practice to round up to the nearest 10. At that point, he said there's a 50 THB service charge for using the meter. I didn't buy that, but I went ahead with it and added 20 THB (which I had from the change given at the toll booths).

So, I arrived at the Airbnb, checked in, and.. time for some sleep! I don't sleep well on planes, so I was pretty exhausted.

I hope this helps anyone else feeling anxious about getting to Thailand! It's kind of what I would have liked to have read prior to my trip, but obviously things worked out well for me.

Good luck in your travels!

Saturday, October 1, 2016

You are NOT a burden


"I just don't want to be a burden."

How often have you told yourself that and hidden your feelings, fears, and pain from loved ones?

Earlier this week, I was talking with a friend who's been through a lot the past couple years. As we discussed coping with life's challenges by relying on loved ones, she said, "I just don't want to be a burden."

Relying on loved ones can be scary.

For so many of us, something in our past frightened us into thinking that sharing our pain with others puts an unconscionable weight on their shoulders. We fear becoming an inconvenience. We fear they'll get tired of us and reject us.

And that's true. They would reject us... if we really were the people we perceive ourselves to be.

But we're not. Mirrors are ugly. We see too many blemishes and scars to love ourselves and the beauty of the person we really are; the one we've hidden from the world.

We all have our ups and downs. Sometimes the crushing weight of the world drives us into despair and sometimes we find ourselves soaring above it.

When we love someone, we don't just experience their sorrow. We also experience their joy. And we experience their peace and their chaos and their awe and their anger and so much more. That's what makes us all human and we fall in love with humans, not robots.

Not only did I see this in the mentioned conversation, but over the past two weeks, I saw it in two other conversations with different friends. I see it in myself, over and over again.

For as long as I can remember, I buried my negative emotions where no one could see them. I suppressed my feelings in fear of burdening loved ones. I was scared that if they knew me, they wouldn't like me; that I'd be nothing but a weight on their shoulders.

I felt I wasn't worthy of being loved. So I hid behind perpetual "happiness".

The great irony is that what brought me authentic happiness was being there for the people I love in both joyous moments and painful ones. It's a gift to share in another's woes as much as it's a gift to share their abundance.

I'm not a burden to those I love any more than they're a burden to me.

You are NOT a burden.

To my family and my friends, I love you and you're the joy of my life. Let me be there for you just as you want to be there for me. Consider it a gift to me. It's what I yearn for more than anything in this world.


Let your sorrow carve a valley into my soul,
That I may fill it with the joy of love.

Saturday, March 12, 2016

Fuck it, hit publish now

One of the privileges my work and community involvement offer me is interaction with a large number of interesting people. About once a month, someone I meet will tell me they read something from this blog and that it had a meaningful impact on them.

In fact, this happened about 2 hours ago at a hackathon we're hosting at my workplace. One of the participants introduced himself and said he appreciated my post about how I royally screwed up in school. He shared how he went through a similar challenge, failing several classes and thinking he should just drop out, before finding his passion and turning things around. Like me, he ended up at one of the world's top software companies.

That conversation meant a lot to me.

Despite these kinds of experiences, I haven't posted in over 9 months. Still, I jot down notes all the time and have over 25 pending posts that are half-written. So, why haven't I posted?

The reasons mentioned in my first blog post, titled "Why Blog?" and published Nov 2011, are still relevant. Shared experiences are valuable. In fact, I've learned that there's even more value in creating content than I originally saw. Sharing experiences publicly has given me amazing opportunities to connect with and learn from others.

I think my biggest issue has been with agonizing over perfection. I spend hours writing, re-writing, regretting, revising every post. In that original post I said, "anyone can take 15 minutes while waiting for the bus to jot down a few thoughts." What I didn't realize at the time was that I'd never feel satisfied enough to publish after 15 minutes. Or 30 minutes. Or 120 mintues.

I crave more experiences like the one I described at the beginning of this post. Opportunities to form deep connections with others means the world to me.

I'm going to give two things a shot for a while. First, I'm going to relax my standards to a point where I'm less comfortable publishing, but still willing to hit the publish button. Second, I'm going to write on a regular basis, starting with 15 minutes a day, 5 days a week. Over time, I'm hope to see a rise in quality and a fall in time spent per published post post.

Well, it's been about 1.5 hours since I started working on this. Fuck it. I'm hitting publish now...

[Edit: 2 minutes after hitting publish, I saw that I'd forgotten to give the post a title. *sigh* Well... that gives me a good idea for a title.]