os.path.expanduser
The shell expands ~ and $HOME for you; Python's open() does not. These two functions do the expansion yourself. Both read the environment of the running process, so their result is different on every machine: os.path is posixpath on Linux and macOS (HOME, $name) and ntpath on Windows (USERPROFILE, %name% as well).
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| path | str | bytes | os.PathLike | yes | The path to expand. A path-like object (3.6+) is accepted; the result is always str or bytes, never a Path. |
Return value
str | bytes — The path with ~ (expanduser) or $variables (expandvars) replaced; unchanged when nothing could be expanded.
Common patterns
import os config = os.path.expanduser('~/.config/myapp/config.toml')
import os def expand(path): return os.path.expanduser(os.path.expandvars(path))
from pathlib import Path config = Path('~/.config/myapp/config.toml').expanduser() home = Path.home()
import os log_dir = os.path.expandvars('$APP_LOG_DIR') if log_dir.startswith('$'): raise SystemExit('APP_LOG_DIR is not set')
Examples
Pitfalls
try: open('~/notes.txt') except OSError as e: result = type(e).__name__ result
import os, posixpath from unittest import mock with mock.patch.dict(os.environ, {'HOME': '/home/ada'}): result = posixpath.expanduser('~/notes.txt') result
import os, posixpath from unittest import mock with mock.patch.dict(os.environ, {'NAME': 'ada'}): result = posixpath.expandvars('$NAME_backup') result
import os, posixpath from unittest import mock with mock.patch.dict(os.environ, {'NAME': 'ada'}): result = posixpath.expandvars('${NAME}_backup') result
import os, posixpath from unittest import mock with mock.patch.dict(os.environ, {'HOME': '/home/ada'}): result = posixpath.expanduser('$HOME/x'), posixpath.expandvars('~/x') result
import os, posixpath from unittest import mock with mock.patch.dict(os.environ, {'HOME': '/home/ada'}): result = [posixpath.expanduser(posixpath.expandvars(p)) for p in ['$HOME/x', '~/x']] result
When to use
- Turning user-typed or config-file paths like ~/data or $XDG_CONFIG_HOME/app into real paths
- Building per-user default locations for config, cache and history files
- Reading one variable → os.environ.get(name) tells you when it is missing
- Untrusted input → expandvars can pull any environment value into the path
- Object-oriented code → Path.expanduser() / Path.home() (no expandvars equivalent)
Notes
FAQ
Because it is different on every machine. The examples set HOME or USERPROFILE inside unittest.mock.patch.dict(os.environ, ...) and call posixpath or ntpath directly, so the output is the same everywhere. In your own code just call os.path.expanduser, which is posixpath.expanduser on Linux and macOS and ntpath.expanduser on Windows.