os/user
Who is running the program, and the standard directories that belong to them.
import os.user
The os.user package answers two questions: who is running the program (Username, Uid, Gid), and where that user's files belong (HomeDir, ConfigDir, CacheDir). Programs usually keep their settings in the user config directory and throwaway data in the user cache directory; this package gives the conventional path on each system, for instance ~/.config on Linux and %APPDATA% on Windows. Note that macOS also gets ~/.config and ~/.cache, the convention of command line tools, rather than ~/Library.
For the machine rather than the user, see os.platform; for reading other environment variables, env; for joining paths onto these folders, fs.path.
How the directories are found
These functions do not ask the operating system for a list of folders. They read environment variables, the same way most command line tools do, which also means that whoever starts the program can point them somewhere else:
| Function | Linux, macOS and BSD | Windows |
|---|---|---|
HomeDir | $HOME | %USERPROFILE% |
ConfigDir | $XDG_CONFIG_HOME, else ~/.config | %APPDATA% |
CacheDir | $XDG_CACHE_HOME, else ~/.cache | %LOCALAPPDATA% |
Username | $USER, else $LOGNAME, else the user database | %USERNAME% |
The example sets the variables itself with env.Set, so it prints the same on every Unix machine:
import env
import os.user
func main:
env.Set("HOME", "/home/sara")
env.Unset("XDG_CONFIG_HOME")
env.Unset("XDG_CACHE_HOME")
println "home: ", user.HomeDir()
println "config:", user.ConfigDir()
println "cache: ", user.CacheDir()
env.Set("XDG_CONFIG_HOME", "/data/sara/config")
env.Set("XDG_CACHE_HOME", "/fast-disk/cache")
println "config:", user.ConfigDir()
println "cache: ", user.CacheDir()
endSettings and cache for your own program
Give your program its own folder inside each of these directories, named after the program, and create it with dir.MkdirAll before the first write: on a fresh account even ~/.config may not exist yet. Settings belong in the config folder. Anything you can rebuild, such as downloads or a search index, belongs in the cache folder, which the user or a cleanup tool may empty at any time.
The first line of this example points HOME at a folder in the current directory so that running it does not touch your real settings. A real program leaves that line out.
import env
import fs.dir
import fs.file
import fs.path
import os
import os.user
func main:
env.Set("HOME", path.Join(os.Cwd(), "demo-home"))
env.Unset("XDG_CONFIG_HOME")
env.Unset("XDG_CACHE_HOME")
config := path.Join(user.ConfigDir(), "notes")
cache := path.Join(user.CacheDir(), "notes")
dir.MkdirAll(config)
dir.MkdirAll(cache)
settings := path.Join(config, "settings.ini")
repeat 2 in run:
if file.Exists(settings):
println "run", run + 1, "found", file.ReadText(settings).trim()
else:
file.WriteText(settings, "theme=dark\n")
println "run", run + 1, "wrote the default settings"
end
end
file.WriteText(path.Join(cache, "index.bin"), "0123456789")
println "settings at ~/" + path.Rel(user.HomeDir(), settings)
println "cache holds", dir.Size(cache), "bytes"
dir.RemoveAll(user.HomeDir())
endExpanding ~ in paths from users
The shell turns ~/notes into a full path before your program sees it, but a path read from a settings file or typed at a prompt still starts with ~. Neither fs.file nor fs.path expands it, so replace it yourself with HomeDir:
import env
import fs.path
import os.user
import str
func expand(p: str): str:
if p == "~": ret user.HomeDir() end
if str.StartsWith(p, "~/"):
ret path.Join(user.HomeDir(), p.substr(2, p.len() - 2))
end
ret p
end
func main:
env.Set("HOME", "/home/sara")
each p in ["~", "~/notes/today.md", "/etc/hosts", "reports/may.csv"]:
println p, "->", expand(p)
end
endThings to watch for
Usernameis fine for a greeting or a log line, but it comes from environment variables that anyone starting the program can change. Never use it to decide what someone is allowed to do.UidandGidare-1on Windows. On Unix aUidof0means the program runs as root, which is worth a warning in a tool that should not.- When its variable is not set, for example in a service started with an empty environment,
HomeDirreturns an empty string, andConfigDirandCacheDiron Unix then return the relative paths.configand.cache. CheckHomeDirfirst; otherwise your files silently land in the current directory.
Functions
Username
func Username(): str$USER, else $LOGNAME, else the system user database; %USERNAME% on Windows. It can be changed by whoever starts the program.import os.user
func main:
println user.Username().len() > 0
println user.Uid() >= 0, user.Gid() >= 0
println user.HomeDir().len() > 0
println user.ConfigDir().len() > 0, user.CacheDir().len() > 0
endUid
func Uid(): intThe example for this item is shown with the ones above: os-user-Username.salam
Gid
func Gid(): intThe example for this item is shown with the ones above: os-user-Username.salam
HomeDir
func HomeDir(): str$HOME (%USERPROFILE% on Windows); empty when the variable is not set.The example for this item is shown with the ones above: os-user-Username.salam
ConfigDir
func ConfigDir(): str$XDG_CONFIG_HOME or ~/.config, %APPDATA% on Windows. It may not exist yet.The example for this item is shown with the ones above: os-user-Username.salam
CacheDir
func CacheDir(): str$XDG_CACHE_HOME or ~/.cache, %LOCALAPPDATA% on Windows. It may not exist yet.The example for this item is shown with the ones above: os-user-Username.salam