Justfile takes the pain out of reading files in Python

Written by

Writing files in Python isn’t terrible, but it’s not convenient, either. With-syntax can get cumbersome inside other constructs and there’s a lot of overhead before you even get to reading the file!

Thankfully, the justfile library makes it easy by “just reading” or “just writing” to a file. It makes assumptions that can be easily overriden based on need.

Let’s take a look!


Full Disclosure…

Full disclosure, this is my library. I just figured I’d promote it while showing examples of file i/o in Python.

The Boring/Painful/Hair-pulling/Excruciating Way

This is how official Python tutorial says to read a file:

with open('workfile') as f:
    read_data = f.read()

If you’re new to programming this probably looks like a bunch of gobbledy-gook. If you’re a veteran you may find this annoying but still understand what’s happening and you understand the reasoning behind it

Oh, but wait…we want to add error checking if the file doesn’t exist. Or if we can’t open it.

Let’s add some more code.

try:
    with open('workfile') as f:
        read_data = f.read()
except FileNotFoundError as e:
    print("not found");

Okay, whew! At least that’s over. Just imagine if we had to write to a file. Ah, what the heck! Let’s do it.

with open('workfile', 'w+') as f:
    f.write("hello world")

Not too bad. But we still have some errors to contend with:

  • The path to the file doesn’t exist.
  • The file already exists
  • We don’t have permission to write a file there.

Okay, that’s no problem, right? We just have to add about 10 more lines of code. No biggie!

import os
OUTFILE = "/home/disgruntledprogrammer/somedir/somefile.txt"
if not os.path.exists(os.path.dirname(OUTFILE)):
    os.makedirs(os.path.dirname(OUTFILE))
if os.path.exists(OUTFILE):
    raise RuntimeError("file exists")
with open(OUTFILE, 'w+') as f:
    f.write("hello world")

And then your boss comes to you and asks you to read a config file and write out data to an XML file. Then in a fit of rage you grab your entire workstation march over to the window and throw it out onto the street 3 floors below. While this action doesn’t result in physical injury the company fines you $10,300 for destruction of property. But by the time they fine you you’re already on the run, living out of your van with an unregistered license plate, selling potato chips on the black market and using burner phones to call your mom to tell her you’re alright but you can’t make it for Christmas this year. But that’s a story for another time.

As you can see all we’re trying to do is write to a file, yet we have a lot of overhead to make it work.

Enter justfile

I realized one day, why can’t I “just write” to a file, or “just read” to it without having to wory about all these edge cases?

So that’s why I created justfile. It’s a library that allows you to simply read and write without worrying about error checking.

Installation

You can install include justfile in your projects the way you would any other python package:

pip install justfile

Reading

Here’s an example:

from justfile.io import read
print(read("file-does-exist"))
# Hello, World!
print(read("file-does-not-exist"))
# None

None?” you say. “But that breaks all programming conventions!”

Don’t worry, Exception fan, I hear you.

So if you’re the type that wants there to be an error thrown if somethong goes wrong, I’ve got you covered!

from justfile.io import read
print(read('file', complain=True))                         
# ---------------------------------------------------------------------------
# JustFileException                         Traceback (most recent call last)
# <ipython-input-5-6ded5f7518eb> in <module>
# ----> 1 print(read('file', complain=True))
#
# ~/.local/lib/python3.8/site-packages/justfile/io.py in read(path, mode, complain, default_content)
#      25                 if not complain:
#      26                         return default_content
# ---> 27                 raise e
#      28 
#      29 def write(path, content, mode="w+", create_dirs=True, complain=False):

# ~/.local/lib/python3.8/site-packages/justfile/io.py in read(path, mode, complain, default_content)
#      17         try:
#      18                 if not os.path.exists(path):
# ---> 19                         raise JustFileException(f"{path} does not exist.")
#      20                 if not os.path.exists(path):
#      21                         return None
# 
# JustFileException: file does not exist.

Bear with me here, though. There is an advantage to keeping complain=False. This allows you better control over how not having the file is handled.

For example, by providing a default config:

from justfile.io import read
CONFIG = parse_yaml(read("config.yaml") or """
x: 1
y: 4""")

Or warning the user that a file is missing:

from justfile.io import read
if not read("config.yaml"):
    print("Config is missing")

Writing & Appending

Writing to a file is just as simple.

from justfile.io import read, write
write("somefile.txt", "Hello, World!")
print(read("somefile.txt"))
# Hello, World!

No directory? No problem?

from justfile.io import read, write
write("./path/to/some/somefile.txt", "Hello, World!")
print(read("./path/to/some/somefile.txt"))
# Hello, World!

Unless you don’t want to create directories for some reason…

from justfile.io import read, write
write("./path/to/some/somefile.txt", "Hello, World!", create_dirs=False)
print(read("./path/to/some/somefile.txt"))
# None

And remember, you can always complain…

from justfile.io import read, write
write("./path/to/some/somefile.txt", "Hello, World!", create_dirs=False, complain=True)

"""
FileNotFoundError                         Traceback (most recent call last)
<ipython-input-12-7a87c377d6d8> in <module>
----> 1 write("./path/to/some/somefile.txt", "Hello, World!", create_dirs=False, complain=True)

~/.local/lib/python3.8/site-packages/justfile/io.py in write(path, content, mode, create_dirs, complain)
     51         except Exception as e:
     52                 if complain:
---> 53                         raise e
     54                 return False
     55 

~/.local/lib/python3.8/site-packages/justfile/io.py in write(path, content, mode, create_dirs, complain)
     46                 if not os.path.exists(d) and len(d) and create_dirs:
     47                         os.makedirs(d)
---> 48                 with open(path, mode) as f:
     49                         f.write(content)
     50                         return True

FileNotFoundError: [Errno 2] No such file or directory: './path/to/some/somefile.txt'
"""

Keep in mind this overwrites a file. What if you want to append to a file?

There’s also an append function for just that purpose!

from justfile.io import (read, write, append)
write("file.txt", "Hello, World!")
append("file.txt", " This is Sparta!")
print(read("file.txt"))
# "Hello, World! This is Sparta!"

Conclusion

As you can see, reading and writing files in python doesn’t have to cause a complete mental breakdown. Using the justfile library can help with file i/o and handle a lot of the edge cases that are messy.

Note that I wouldn’t use justfile for every instance of file i/o. For example, it won’t handle chunking data very well, and it expects you to pass it a path instead of a file handle (so using sys.stdout wouldn’t work for instance).

But for the vast majority of cases reading plain text, it’s just the ticket!

Did you like this article?

Did you know I'm available for hire?

Send me an email here: hireme@jordanhewitt.pro

Or connect with me in Wire: @nswered