Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@askpatrickw
Last active March 15, 2024 21:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save askpatrickw/610a83c489c6588055f6d1d3fd933fa7 to your computer and use it in GitHub Desktop.
Save askpatrickw/610a83c489c6588055f6d1d3fd933fa7 to your computer and use it in GitHub Desktop.
CircuitPython Secrets

CircuitPython Secrets

This are some notes around the use of secrets.py in CircuitPython.

Current Events

Today CP examples suggest a secrets.py files for storing passwords, SSIDs, keys, etc as a dictionary.

File: secrets.py

secrets = {
key = value,
key1 = value1
}

Usage of this looks like so:

from secrets import secrets

print(secrets['key'])

Issues with Current Events

  1. Secrets is a core module in Python, so we're teaching new coders a practice which will surely leads to confusion if\when they move on to Python coding.
  2. Using a dictionary is not wrong, but combersome and lacks the helpful functionality other approaches would have, such as autocomplete.

Potential Approaches

New File Name

At a minimum, suggesting the use of secrets.py should be changed. The good thing about that name though is that it does imply that it is information you don't want to put into your GitHub repo. Some potential new file names:

  • config.py <-- This could be for all configuraion you can commit.
  • config_secrets.py <-- this could be for all configuration you should NOT commit.

Adding config.secrets.py to the .gitignore should also be recommended and added to .gitignore tools.

Question: Should the recommendation also be use the CAPS style since these are truly globals? ex: config_secrets.KEY1

New Data Format

The dictionary format could be improved. Using variables would allow for dot notation and autcomplete.

file: config_secrets.py

key = value
key1 = value1

file: code.py

import config_secrets.py

print(config_secrets.key1)

Environment Variables in CPython and MicroPython

In CPython, it is common to use OS environment variables and the OS Core Module to work with the values:

  • os.get_env: os.getenv(key, default=None) returns the value of the environment variable or the supplied default.
  • os.environ: Tuple-like string mapping of environment variables at thetime of os import. Updating the object also updates the environment and is preferred to calling os.putenv or os.unsetenv directly.
  • os.putenv: Creates or updates an evironment variable, does not update os.environ.
  • os.unsetenv: Deletes an environment variable, does not update os.environ
  • os.environb: Bytes version of os.environ. Linux Only
  • os.get_envb: Bytes version of os.get_env. Linux Only

os.get_env() is the very common method usesin CPython applications.

There are also many environment libraries for CPython and frameworks (Django, ertc..) to help manage not only run-time but dev, test, production environments.

In MicroPython there is nothing implemented in the MP core libraries. There is an external library ShenTengTu/micropython-env which doesn't appear to be in wide use (2 ⭐️). The approach in ShenTengTu/micropython-env is to import from JSON files or MessagePack

More Reasearch

  1. It was suggested by @tannewt, that CP could use an approach similiar to the environment variable approach. How these are set initially is still an unknown to me. I could see os.put_env() being called from the REPL or from boot.py to set values from config_secrets.py. for example: os.load_env(config_secrets) and then using os.get_env().
  2. Are values preserved across reboots is an interesting discussion...
@askpatrickw
Copy link
Author

python dotenv is another great tool to look at ...

@evenprimes
Copy link

I just stumbled across this the other day. I'm new to CP, but not to Python. I think "training" people from the start to not use a filename that's likely to conflict with something in the standard library is a really smart change. Especially since CP is targeting beginners, having a random name collision is not great.

Of the options you present for changing the contents of the file, just making things be global module variables seems the most Pythonic. The import statement is straightforward and keeping the format simple and readable seems like a win. Loosing the ability to use a default in os.get_env() does seem like a minor loss, but I don't think this is as valuable as keeping the syntax and process simple.

This is effectively embedded Python. Complicating things with having to os.load_environ() and use os.get_env() seems way overkill. Just being able to reference config_secrets.NASA_API_KEY is simple and easier for a beginner to understand.

Just to be clear, I don't think we should prohibit the use of other options, but for getting new/beginner programmers up and running with CP, a simple solution is probably best.

@askpatrickw
Copy link
Author

@evenprimes all good comments, thank you. You might want to mention your thoughts in the Circuit Python Discord.
However, the biggest hindrance to change at this point is updating all the CP learning guides and samples.

@evenprimes
Copy link

Actually, it might be even bigger than that. The specific project I was working on when I found this was a PyPortal (from Adafruit) dashboard. At least in the Portal() class, and probably elsewhere in CircuitPython, the libraries assume that a global secrets dict will exist and be populated with certain keys.

There seems to be at least a little awareness that this is an issue based on (not currently updated in the documentation) changes in keyword parameters. I'll probably jump over there to mention this once I get a little more time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment