Tidbits | Feb. 22, 2023

Pro-Tip – Formatting Gone Wrong

by Kojo Idrissa |   More posts by Kojo

I posted this tweet after feeling defeated by trying to set up SendGrid with Django. I kept getting a smtplib.SMTPServerDisconnected: Connection unexpectedly closed error and had NO IDEA why.

Turns out, it was my editor. I had two settings that I usually depend on, but today, they were working against me.

Background

I've been going through Will Vincent's "Django For Beginners" book to help catch gaps in my knowledge. It's a great book and Will's a great person. In addition, I often get asked where/how to start learning Django, so I wanted to go through the book in more detail, so I could make a stronger, more informed recommendation. QA isn't just for software! Spoiler alert: It's a great book and Will's a great person.

In Chapter 1, Will has you set up VS Code as your editor (but it could have happened with most editors). He tells you to configure it to do two things:

  • Setup black to be your python formatting provider (an excellent suggestion)
  • Configure your editor to format on save (an excellent suggestion)

I already had VS Code configured to do one more thing, and this is the thing that bit me: Autosave on focus change.

Lots of people use this combination of settings. Automatically linting and formatting your code when you save a file can help you avoid CI fails and having to make commits with messages like "fixed linting". And if you were to ask me, I'd suggest you use black as your Python formatter. It's what the Django codebase uses. Here's where things went wrong for me:

  • an API key
  • not following along closely enough!

API Keys: They're Sensitive

Jumping ahead to Chapter 12, I was setting up my Django project to be able to send emails using SendGrid. While setting up SendGrid, I had to generate an API key, then add it to my settings.py file, so my Django project could connect to the service. When you generate an API key, there's an instant of panic where you think, "Ok, let me copy/paste this somewhere so I don't lose it!" A common approach is to just paste it into the file where it'll be used, then assign/use it later. Since this was a practice project, I was more concerned with, "Does this work?" than application security. So, I hard-coded the API key into my settings file:

EMAIL_HOST_PASSWORD = MY.secret-API.key-dont-look

But, since it was a Python file, black took one look, said, "That's not right!" and 'fixed' it. So, as soon as I switched to another window (remember: I've turned on Autosave on focus change, which was not one of Will's suggestions), black turned it into this:

EMAIL_HOST_PASSWORD = MY.secret - API.key - dont - look

I didn't notice the change, but if you know anything about API keys, those spaces make a difference. Now I was using an invalid API key and wondering why I couldn't connect. After some time pairing with Jeff, he realized there were way too many spaces in that API key. 🤦🏾‍♂️

How To Avoid This?

There are a few approaches I could have taken.

Hard-code the API key as a string

This is exactly what Will suggested! When I go back and look at the screenshot of settings.py in his book, I see:

EMAIL_HOST_PASSWORD = "MY.secret-API.key-dont-look".

black won't change the contents of a string literal. That would have worked in the short term (Remember kids: don't put your API keys into files you commit to Git!).

Will literally reminds you of this in the paragraph after the settings.py screenshot. I have that entire paragraph highlighted. He tells you, (in Chapter 12), In Chapter 16 we will learn how to add environment variables to our project so that we can keep secret information truly secret. So, if I'd followed the screenshot more closely, I'd have been finished with this mid-afternoon Friday.🤦🏾‍♂️

Disable autosave on focus change

I go back and forth on this. The convenience of not forgetting to save a change is nice. However, there's also something to be said for being mindful and intentional about saving your work. This is especially true if you have something else set to run when you save. For now, I've opted for convenience. I could also disable format on save, but I don't like that option. If I'm going to save a change, I want it formatted.

Current best practice

The approach I took was to do what Will foreshadowed from Chapter 16. I made the API key an environment variable and set the project up to use it. Don't ever think you're too fancy to remember your 12 Factor App fundamentals, even on practice projects. In this case that means:

  • adding a .env file of some sort, to store my secret
  • making sure that file does NOT go into version control
  • importing something into settings.py to allow it to read environment variables

Again, this is part of the challenge of being self and community-taught. Going through a book for beginners, there are times I already know to do things that will come later, and there are things that are new to me that might be old hat for others.

The Final Product

I'm using direnv, so I made a .envrc file that contains:

export EMAIL_HOST_PASSWORD = "MY.secret-API.key-dont-look".

I've also added a .envrc line to my .gitignore file.

As a team, we like django-environ over os.environ (but os.environ is built-in to Python and it will also get the job done). So my settings.py looks like this:

# settings.py
import environs
env = environs.Env()
...
*** various Django settings ***
...
EMAIL_HOST_PASSWORD = env("EMAIL_HOST_PASSWORD")

And there you go: it all works now! Teamwork Makes The Dream Work (thanks to Lacey for all the helpful input)! And Reading Is Fundamental!

Your code formatter may have reformatted your API key. This could cause many confusing errors.{% else %}

2023-02-22T11:30:00 2023-02-22T11:31:08.048280 2023