Nik Kantar

Wednesday, February 24, 2021
3 min read

Introducing Parsenvy

I made an environment variable parsing library for Python, and forgot to tell you all about it!

I’m laughing as I type this, since this is the third post about Parsenvy I’ve written in the last six weeks. First I wrote a draft of an introduction much like this one, long overdue. Throughout the process, I started questioning the point of the library in the first place, especially as I don’t really use it much myself any more, so I wrote one wondering about its future.

I didn’t get around to finishing up either one of those before PyCascades, where a few people ended up sprinting on it (check out the sweet docs!), which got me to finish up v3. So now you get this third version.

I’m actually mildly shocked I never wrote this post, since Parsenvy has been around _for a while_—the first commit landed on March 16, 2017, and the first release was installable on July 31, 2017.

What is Parsenvy anyway?

It’s a Python library for reading environment variables and typecasting them into some of the built-in types—namely the “simple” ones: numerics, sequences, and mappings. The various tools in the standard library’s os module always return strings (unless a default of a different type is specified), so if you need an integer, you have to do something like this:

from os import getenv
DEFAULT_PAGE_LENGTH = int(getenv("DEFAULT_PAGE_LENGTH", 10))

If you omit the int() call, you’ll get a string—e.g., "7"—instead of integer—e.g., 7—from the environment, if the variable is defined. This is hardly groundbreaking, but it can read a bit clumsy if you need a lot of it.

With Parsenvy it reads a little better:

import parsenvy
DEFAULT_PAGE_LENGTH = parsenvy.int("DEFAULT_PAGE_LENGTH", 10)

But it improves if there’s a block of these:

from os import getenv
DEBUG = bool(getenv("DEBUG", False))
DEFAULT_PAGE_LENGTH = int(getenv("DEFAULT_PAGE_LENGTH", 10))
HOSTNAME = getenv("HOSTNAME")

# versus

import parsenvy
DEBUG = parsenvy.bool("DEBUG", False)
DEFAULT_PAGE_LENGTH = parsenvy.int("DEFAULT_PAGE_LENGTH", 10)
HOSTNAME = parsenvy.str("HOSTNAME")

What does Parsenvy support?

Parsenvy supports some of the most commonly used built-in types:

Here’s the example section of the README:

>>> import parsenvy

# DEBUG_ENABLED=True
>>> parsenvy.bool("DEBUG_ENABLED")
True

# POSTS_PER_PAGE=13
>>> parsenvy.int("POSTS_PER_PAGE")
13

# EXCHANGE_RATE=42.911
>>> parsenvy.float("EXCHANGE_RATE")
42.911

# INVALID_USERNAMES=admin,superuser,user,webmaster
>>> parsenvy.list("INVALID_USERNAMES")
["admin", "superuser", "user", "webmaster"]

# SAMPLE_GREETING=Hello,world!
>>> parsenvy.tuple("SAMPLE_GREETING")
("Hello", "world!")

# ALLOWED_CATEGORIES=python,vim,git
>>> parsenvy.set("ALLOWED_CATEGORIES")
{"python", "vim", "git"}

# DB_PREFIX=dj_
>>> parsenvy.str("DB_PREFIX")
"dj_"

What about other types?

Short answer: some combination of “I haven’t needed them yet”, “couldn’t come up with good syntax”, and “just plain forgot about them”.

There’s an issue to add support for range already, and bytes, bytearray, dict, memoryview, and frozenset are under consideration in another. There’s also the possibility of adding support for shell data structures, but that’s enough of a change to warrant a new major version, and I’m in no rush.

Feedback is welcome on any of the above issues, any other ones, or even new ones. After all, it’s a community project now. ;)


Tags: projects, python

Thanks for reading! You can keep up with my writing via the feed or newsletter, or you can get in touch via email or Mastodon.


Older:
Django Site Dispatch, Improved
Newer:
Newsletter, Again