Work with File Paths in Python

Learn how to construct, join, and navigate file paths in Python using os.path and pathlib, so your programs find files reliably across operating systems.

5 min read

When you pass a bare filename like "data.csv" to the open function, Python looks for that file in the current working directory. This works for simple scripts that live in the same folder as their data, but real projects quickly outgrow that assumption. Your script might live in a src folder while the data lives in a data folder. You might need to read a configuration file from the user's home directory, process every file in a nested folder tree, or generate output paths that work on Windows, macOS, and Linux without manual changes. Python file paths are the mechanism that tells open exactly where on disk a file lives, and Python provides two robust APIs for constructing and manipulating them: the older os.path module and the modern pathlib module introduced in Python 3.4.

Before you can open a file, you need a path that accurately describes its location. A path that works on your development machine but breaks on a colleague's computer or a server is a path that was not constructed properly. This article covers the two path APIs, explains when to use each one, and gives you patterns that produce correct paths regardless of which operating system runs your code. If you have read the articles on opening and closing files in Python and file modes in Python, you already know how to open files once you have a path. This article completes the picture by showing you how to build that path reliably.

Absolute paths and relative paths

An absolute path describes a file's location starting from the root of the file system. On macOS and Linux, the root is a single forward slash, and an absolute path looks like /home/alex/projects/data/input.csv. On Windows, the root is a drive letter like C:, and an absolute path looks like C:\Users\alex\projects\data\input.csv. Absolute paths are unambiguous: no matter what the current working directory is, the same absolute path always points to the same file on the same machine. The tradeoff is that absolute paths are not portable. A path that starts with /home/alex does not exist on a machine where the user is named jordan, and a path with C:\ does not exist on macOS or Linux at all.

A relative path describes a file's location relative to the current working directory. If the current working directory is /home/alex/projects, the relative path data/input.csv points to /home/alex/projects/data/input.csv. A relative path that starts with two dots moves up one directory level: ../config/settings.json from /home/alex/projects points to /home/alex/config/settings.json. Relative paths are portable because they do not embed machine-specific details like usernames or drive letters. As long as your project maintains a consistent internal folder structure, relative paths from the project root work on any machine.

pythonpython
from pathlib import Path
 
cwd = Path.cwd()
print(f"Current directory: {cwd}")
 
relative = Path("data/input.csv")
absolute = relative.resolve()
print(f"Absolute path: {absolute}")

The Path.cwd method returns the current working directory as a pathlib Path object. The resolve method converts a relative path to an absolute one by prepending the current working directory and resolving any dot and dot-dot segments. This is the standard pattern for turning a project-relative path into a full path that open can use, and it works identically on Windows, macOS, and Linux because pathlib handles the platform-specific separator character automatically.

Building paths with pathlib

The pathlib module represents file system paths as objects rather than strings. You create a Path object by passing a string to the Path constructor, and you build longer paths by using the forward slash operator between Path objects and strings. This operator overloading is one of the most elegant features of pathlib: it reads like natural path construction and automatically inserts the correct separator for the current operating system.

pythonpython
from pathlib import Path
 
project = Path("my_project")
data_dir = project / "data"
input_file = data_dir / "input.csv"
 
print(input_file)
print(input_file.exists())
print(input_file.is_file())

The expression project / "data" joins the Path object and the string with the platform-appropriate separator, producing my_project/data on macOS and Linux or my_project\data on Windows. The exists method returns True if the path points to something on disk, and is_file returns True specifically if it is a regular file rather than a directory. These methods let you check whether a file is present before attempting to open it, which avoids FileNotFoundError when the file is legitimately optional.

Pathlib also provides convenient shortcuts for common locations. Path.home returns the current user's home directory regardless of platform, and Path.cwd returns the current working directory. The iterdir method lists the contents of a directory, and the glob method finds files matching a pattern, such as all CSV files in a folder. These features make pathlib suitable not just for constructing individual file paths but for traversing directory trees, filtering files by extension, and building file processing pipelines.

Working with os.path in existing code

The os.path module predates pathlib and remains widely used, especially in codebases that predate Python 3.4 or that need to run on older Python versions. It represents paths as plain strings and provides functions that operate on those strings. The os.path.join function is the equivalent of pathlib's forward slash operator: it joins path components with the correct separator for the current platform.

The os.path module also includes functions for splitting a path into its directory and filename components, extracting the file extension, checking whether a path is absolute, and normalizing a path by collapsing redundant separators and dot-dot segments. The os.path.abspath function converts a relative path to an absolute one, and os.path.exists checks whether a path points to something on disk. These functions work reliably and are well-tested, so there is no urgency to rewrite os.path code into pathlib. But for new code, pathlib is the recommended choice because the object-oriented style produces fewer bugs from accidentally treating a path as a plain string.

pythonpython
import os
 
data_dir = os.path.join("my_project", "data")
input_file = os.path.join(data_dir, "input.csv")
print(os.path.abspath(input_file))

This code does the same thing as the pathlib example above, but the function-call style is more verbose and the intermediate values are plain strings rather than Path objects. Both approaches produce a correct path for the current platform. The choice between them is primarily a matter of codebase consistency and personal preference, with pathlib being the forward-looking option.

Path safety and cross-platform concerns

The most common path-related bug in Python programs is hardcoding a separator character. Writing "data\input.csv" with a backslash works on Windows but produces a filename with a literal backslash in it on macOS and Linux. Writing "data/input.csv" with a forward slash works on macOS and Linux and, surprisingly, also works on modern Windows when passed to Python's open function directly, but it fails when passed to command-line tools or other Windows APIs that expect backslashes. The safest approach is to never type a separator character at all and instead let pathlib or os.path.join construct the path.

Another common issue is assuming the current working directory. When you double-click a Python script in a file manager, the current working directory might be the user's home directory rather than the script's directory. When you run a script from a different folder, relative paths resolve relative to that folder, not the script's location. If your script depends on files in its own directory, use the file variable combined with Path to construct paths relative to the script rather than the working directory. The expression Path(file).parent gives you the directory containing the script, and you can build all other project paths from that anchor.

Path validation is the final piece of path safety. Before passing a path to open, especially a path that came from user input or a configuration file, check whether it exists and whether it is the right type. The resolve method turns relative paths into absolute ones and normalizes dot-dot segments, which prevents directory traversal attacks where a malicious relative path like ../../../etc/passwd escapes the intended directory. For writing new files, ensure the parent directory exists by calling mkdir with the parents and exist_ok arguments on the parent Path before opening the file for writing. These checks add a few lines of code but prevent entire classes of bugs where your program writes to the wrong location or fails silently because a directory does not exist yet.

Rune AI

Rune AI

Key Insights

  • pathlib is the modern, recommended way to work with file paths in Python, providing an object-oriented API.
  • Use Path.cwd() to get the current working directory and Path.home() for the user's home directory.
  • Join path components with the / operator in pathlib or os.path.join() in the older API.
  • Use resolve() to convert relative paths to absolute paths and .exists() to check if a path points to a real file.
  • Never hardcode forward slashes or backslashes; let pathlib or os.path handle the platform separator.
RunePowered by Rune AI

Frequently Asked Questions

What is the difference between os.path and pathlib?

os.path is the older, function-based module that works with paths as plain strings. pathlib, introduced in Python 3.4, represents paths as objects with methods, making code more readable and less error-prone. pathlib is the recommended approach for new Python code because it handles path separators automatically and provides a consistent API across Windows, macOS, and Linux.

How do I get the current working directory in Python?

Use os.getcwd() from the os module or Path.cwd() from pathlib. Both return the directory from which your Python script was executed. The current working directory is where relative paths start from when you pass a bare filename to open().

What is the difference between an absolute path and a relative path?

An absolute path starts from the root of the file system and specifies every directory in the chain, such as /home/user/docs/file.txt on Linux or C:\Users\docs\file.txt on Windows. A relative path describes the file's location relative to the current working directory, such as docs/file.txt or ../data/input.csv. Absolute paths are unambiguous but not portable; relative paths are portable but depend on where the script is run.

Conclusion

Working with file paths correctly is the difference between a script that runs on your machine and one that runs on anyone's machine. pathlib gives you an object-oriented API that handles path separators, joins, and resolution automatically. For new code, prefer pathlib over os.path. For existing codebases, os.path remains reliable and well-understood. Either way, construct paths programmatically rather than hardcoding separators, and test your path logic on more than one operating system.