mrkeyoor.com_
Wed 23 Sept 00:32 UTC
PyPIUtilsupdated 20 Sept 2026

userpath review

userpath permanently adds directories to a user's PATH through a Python API or the `userpath` CLI. On Unix it detects or accepts a shell and appends shell-specific configuration; on Windows it updates the user's Environment registry value and broadcasts a settings-change message. Separate checks report whether a location is active in the current process, configured for a new shell, or waiting for a restart. Version 1.9.2 is a Windows import-error fix and remains the current release from February 2024. The library can append or prepend, but it has no removal operation.

Verdict

userpath 1.9.2 installed in 0.2 seconds and used 1 MB on our box, but every successful write changes user-owned configuration and the package has no removal API. It fits narrow installer flows with explicit shell targets and a separate uninstall plan; use pipx when the real task is Python app installation.

We installed it

Lab card: what happened when we installed userpathScreenshot of userpath documentation
Install✓ · 0.2s2 packages on disk · 1 MB
Importimport userpath in 0.09s · pure Python · requires Python >=3.7
Known vulns0(pip-audit)

Answers from our run

Does userpath install cleanly?

Yes. In a fresh container with an empty cache, pip install userpath finished in 0.2s, leaving 2 packages and 1 MB on disk. pip-audit reported no known vulnerabilities.

What does userpath need to run?

Python >=3.7, and nothing compiled: it is pure Python. In our run import userpath succeeded in 0.09s.

userpath or pipx: which should you use?

pipx: Use it when the actual job is installing and removing Python command-line applications for a user. userpath 1.9.2 installed in 0.2 seconds and used 1 MB on our box, but every successful write changes user-owned configuration and the package has no removal API.

When should you not use userpath?

Your installer must undo every change. userpath exposes append, prepend, and verification, with no CLI or API command that removes an entry.

API stability5/5The 1.x line retains append, prepend, current and new PATH checks, restart detection, and three CLI commands. Version 1.9.2 is a Windows import fix rather than a call-shape change. The `shells` keyword accepts a sequence while the CLI repeats `--shell`, and existing callers can stay small. The missing removal operation is a stable limitation rather than an API that consumers can plan to receive.
Docs2/5The README supplies installation, CLI help, a short Python session, and the MIT license. It demonstrates the distinction between current PATH, new PATH, and restart state. It does not document the startup files each shell may touch, the `shells`, `all_shells`, `home`, `app_name`, and `check` arguments, Windows registry behavior, detection failures, or the absence of removal. Production installers must read source and history.
Maintenance2/5PyPI uploaded 1.9.2 on February 29, 2024 to fix a Windows import error. GitHub shows an unarchived repository with 168 stars, 19 open issues and pull requests, and a last push on June 10, 2024. The code may change infrequently because its scope is small, though shell startup conventions and Windows environment behavior continue to evolve without a recent release signal.
Ecosystem3/5The supplied weekly figure is 5,001,053 downloads, far above the repository's 168 stars, which is consistent with transitive use by packaging and installer tools. Python 3.7 support and cross-platform behavior widen deployment reach. There is no plugin surface, type marker, uninstall protocol, or registry of new shells, so ecosystem value comes from one embedded machine-configuration task.

Use it if

  • A Python application installer places an executable outside the existing PATH and must guide the user across Windows, macOS, and Linux.
  • The installer can name its target shells explicitly and owns tests for the affected startup-file combinations.
  • You need to distinguish an active PATH entry from one that will appear only after a shell restart.
  • Idempotent append or prepend behavior is enough because uninstallation is handled elsewhere.
Skip it if

Setup reality

Our fresh Python 3.12 Bookworm install of userpath 1.9.2 finished in 0.2 seconds. It left 2 packages using 1 MB, and import userpath worked in 0.09 seconds. We measured 1 direct dependency, a pure-Python package requiring Python 3.7 or newer, an MIT license, and 0 known pip-audit vulnerabilities. The distribution does not ship py.typed, so strict typed code needs local declarations or a checked wrapper.

The difficult part begins when an installer calls append() or prepend(). On Unix, shell detection can inspect the parent process and environment, then choose startup files for supported sh, bash, zsh, fish, or xonsh behavior. A package manager or wrapper can obscure the user's real shell. Pass the shells sequence when the installer knows its targets, and use all_shells=True only when writing several configurations is intentional.

in_current_path() reads the PATH inherited by this Python process. in_new_path() starts a shell and observes what a new session would produce, so slow or interactive startup files can slow verification. need_shell_restart() identifies the useful middle state: configuration is written but the current process has not inherited it. Even after a successful append, a subprocess launched from the same Python process may still fail to find the new executable unless you adjust its environment separately.

On Windows, version 1.9.2 includes a fix for an import error. The library writes the user Environment PATH as a registry value and sends the system settings-change notification; shell-selection arguments do not control that global user setting. On every platform, label changes with app_name, set check=True when failure must raise, and design uninstallation before writing because the package cannot remove the line or registry entry it added.

Patterns

Append a user binary directory add-to-path

import userpath

userpath.append('/home/user/.local/bin')

The current Python process keeps its existing PATH. A new shell can see the configured directory after startup.

Place an application directory first prepend-for-priority

userpath.prepend('/opt/mytool/bin')

Prepending can shadow a system command with the same name. Make that priority change explicit in installer messaging.

Separate active, pending, and absent states check-before-writing

location = '/home/user/.local/bin'

if userpath.in_current_path(location):
    print('already active')
elif userpath.in_new_path(location):
    print('configured; restart the shell')
else:
    userpath.append(location)

`in_current_path` reads this process, while `in_new_path` starts a shell and reads the environment it produces.

Report when activation needs a restart prompt-shell-restart

userpath.append(location, app_name='mytool')

if userpath.need_shell_restart(location):
    print('Restart your shell, or run: exec $SHELL')

A successful configuration write does not update `os.environ`; version 1.9.2 reports that pending state separately.

Raise when PATH verification fails fail-loudly

try:
    userpath.append(location, check=True)
except Exception as exc:
    print(f'could not update PATH: {exc}')
    raise SystemExit(1)

Without `check=True`, callers receive a boolean and can accidentally ignore `False`. Installer code should turn failure into a visible result.

Name the shells an installer supports target-specific-shells

userpath.append(location, shells=['zsh', 'fish'])

The Python keyword is plural. Explicit targets avoid parent-process detection when a package manager or wrapper launched the installer.

Write every built-in shell configuration update-every-shell

userpath.append(location, all_shells=True)

This can touch sh, bash, zsh, fish, and xonsh configuration. Windows ignores shell selection because its user PATH is global.

Add several directories in one call add-multiple-directories

import os

locations = os.pathsep.join(['/opt/mytool/bin', '/opt/mytool/libexec'])
userpath.append(locations)

The function splits `locations` on the platform path separator. With checking enabled, each resulting entry is verified.

Identify the installer in generated comments label-the-change

userpath.append('/opt/mytool/bin', app_name='mytool')

`app_name` labels the Unix configuration addition. The default label is `userpath`, which does not identify the product that requested it.

Modify and verify PATH from the CLI use-cli

userpath append ~/.local/bin
userpath prepend /opt/mytool/bin --shell zsh --shell fish
userpath verify ~/.local/bin

Append and prepend can return exit code 2 when the location already exists unless `--force` is used; installer scripts should distinguish that state from write failure.

Select another account's home directory target-explicit-home

userpath.append(
    '/opt/mytool/bin',
    shells=['bash'],
    home='/home/deploy',
)

The `home` argument changes file targets, not operating-system ownership. Privileged installers must still leave user-readable and user-owned configuration.

Record enough state for uninstallation plan-for-removal

state_file.write_text(json.dumps({
    'location': location,
    'shells': ['bash'],
    'app_name': 'mytool',
}))
userpath.append(location, shells=['bash'], app_name='mytool')

Version 1.9.2 has no remove function or command. Your uninstaller must locate and reverse the exact configuration change itself.

Alternatives

PackageRegistryPick it when
pipxPyPIUse it when the actual job is installing and removing Python command-line applications for a user.
platformdirsPyPIUse it to locate per-user data or binary directories while leaving PATH modification to the installer.
shellinghamPyPIUse it when shell detection is needed but your application will own every configuration edit and rollback.

More utils guides

lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.