Topic 03

Paths and Where Your Program Runs

Hands-on

"File not found" is usually a lie of omission. The file exists; it just is not where the program is looking. The reason is simple once you know it: a file name on its own is looked up in the folder the terminal is in, not the folder the script is in. This page explains that, shows how to build paths that work on every system, and gives you a line that finds a file next to your script wherever the terminal happens to be.

Think of directions that say "the second door on the left". They work only if everyone agrees where you are standing. A full street address works from anywhere. File paths come in the same two kinds.

Relative and Absolute

A path says where a file is. An absolute path starts from the top of the disk, such as /Users/you/python-course/notes.txt on a Mac or C:\Users\you\python-course\notes.txt on Windows. It means the same thing from anywhere. A relative path, such as plain notes.txt, is looked up starting from the current folder, the one you met with pwd in Chapter 1.

The Current Folder Is the Terminal's

Take the simplest possible reading script, read.py, saved in python-course next to notes.txt.

read.py
with open("notes.txt", encoding="utf-8") as f:
    print(f.read())

When you run python3 read.py from inside python-course, the current folder is python-course, and notes.txt is found there. Run the same script from your home folder, as python3 python-course/read.py, and the current folder is your home folder. Python looks for notes.txt there, does not find it, and raises FileNotFoundError, even though the file is sitting right next to the script.

The same script, run from the wrong folder
you@mac ~ % python3 python-course/read.py
Traceback (most recent call last):
  File "/Users/you/python-course/read.py", line 1, in <module>
    with open("notes.txt", encoding="utf-8") as f:
         ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: 'notes.txt'

So the first question for any file-not-found error is: which folder was the terminal in? pwd answers it.

pathlib: Paths as Objects

Writing paths as plain strings means worrying about slashes: forward on a Mac, backward on Windows. The pathlib module removes that worry. A Path object represents a path, and the forward-slash operator joins parts together, producing the right separator for whichever system the program runs on. Paths also answer questions about themselves.

Building and inspecting a path
>>> from pathlib import Path
>>> p = Path("data") / "notes.txt"
>>> p
PosixPath('data/notes.txt')
>>> p.name
'notes.txt'
>>> p.suffix
'.txt'
>>> p.exists()
False

On a Mac or Linux the object prints as a PosixPath; on Windows it would be a WindowsPath written with a backslash. Your code is the same on both. The last line checks whether the file exists, without opening it, which is a good way to give a friendly message instead of a traceback.

Finding a File Next to the Script

For a file that lives alongside your script, there is a line that works wherever the terminal is. Inside any script, the name __file__ holds the script's own path. Python programmers read the two underscores on each side as "dunder", short for double underscore, so this one is said "dunder file". Path(__file__).parent is the folder containing the script, and joining the file name onto it gives an absolute path that does not depend on the current folder at all.

read.py, found from anywhere
from pathlib import Path

notes = Path(__file__).parent / "notes.txt"
with open(notes, encoding="utf-8") as f:
    print(f.read())

This one line is worth copying now and understanding fully after Chapter 9, where modules and the double-underscore names are explained. It is the standard way scripts find the files that ship with them.

Where a relative path is looked up
Your home folder
terminal here: notes.txt is not found
One folder down
python-course, terminal here: notes.txt is found
Inside it
read.py and notes.txt; Path(__file__).parent finds this folder from anywhere
Common Confusions
  • "A relative path starts from the script's folder." It starts from the terminal's current folder. That is why the same script can work from one folder and fail from another.
  • "Windows paths need doubled backslashes everywhere." Build paths with pathlib and the slash operator, and the separators are handled for you.
  • "FileNotFoundError means the file does not exist." It means it is not at the path used. Check the current folder with pwd, or build the path from the script's location.
Why It Matters
  • Asking "which folder am I running from?" first solves most file errors in seconds.
  • Code that builds paths with pathlib runs unchanged on Windows, macOS and Linux.

Knowledge Check

From which folder is a relative path like notes.txt looked up?

  • The folder that contains the running script
  • The terminal's current folder when the program runs
  • The home folder of whoever is using the computer right now
  • The folder where Python itself was installed on the computer

What does Path("data") / "notes.txt" produce?

  • The text data/notes.txt, as a plain string
  • A Path object for notes.txt inside a folder called data
  • The number that results from dividing the two strings together
  • A new folder called data, created on the disk straight away

A script works when run from its own folder but fails from the home folder. What is the likely cause?

  • The script was saved with the wrong encoding for that folder
  • Python cannot run scripts that are stored inside other folders
  • The home folder blocks programs from reading any files that live elsewhere
  • It opens a file by a relative path, looked up in the current folder

What does Path(__file__).parent give inside a script?

  • The folder that contains the script
  • The folder the terminal is currently in
  • The folder one level above the current folder
  • The first line of the script, as text

You got correct