Change Log

2.0.2

Fixes:

  • CLI parsing and terminal fixes: exact switch names win over abbreviations, clear errors for --flag=value on no-argument switches and for unknown requires/excludes names, Set no longer validates the unsplit CSV string, --helpall no longer corrupts later --help output, ask/choose/prompt no longer loop forever on closed stdin, and wide/tall images render with the right aspect (#824)

  • Subprocess and pipeline lifecycle fixes: bgrun kills and reaps the process when the body raises, pipeline call-time args bind to the source command, leaked pipeline pipes are closed, the async TEE handles long lines and multibyte characters and reaps on failure, equal timeout deadlines no longer kill the timeout thread, and daemon fixes (#822)

  • Path/fs edge cases: RemotePath.unlink no longer removes directories recursively and handles symlinks via lstat, RemotePath.access supports F_OK and requires every requested permission bit, LocalPath.chown leaves an unspecified owner/group unchanged, and AtomicFile.locked honors blocking=False for the in-process thread lock (#825)

  • Don’t change the global SIGPIPE disposition in the CLI (#834)

  • Keep the return code off the last line of session output (#830)

  • Path.parent no longer raises IndexError on the filesystem root (#827)

  • Quote paths, cwd, and env names in remote shell command lines (#821)

  • colorlib correctness fixes: stdout guard, Color(0), from_ansi, bold-off (#823)

Docs:

  • Update terminal color support link to its permanent home (#829)

  • Update example output to match the code (#828)

Internal:

  • Remove a redundant class definition and fix indentation (#832)

  • Bump actions/checkout and astral-sh/setup-uv (#826, #831, #833)

2.0.1

Fixes:

  • Make SwitchInfo hashable again so requires=/excludes= work (#818)

Internal:

  • Drop unused conda.recipe (#819)

2.0.0

Features:

  • Support async mode (#768)
    • Support popen on async pipelines (#795, #804)

  • More pathlib API supported (#779)

  • Support color string processing (#774)

  • Remove pywin32 dependency and replace WinAPI calls with a custom ctypes wrapper (#766)

  • Full static typing
    • Add paramiko machine typing (#746)

    • Add remote typing (#744)

    • Add sshmachine typing (#745)

    • Add typing for color (#730)

    • Add typing for fs (#737)

    • Add typing for local machine (#743)

    • Typing paths (#733)

    • Adding rest of typing (#739)

    • Enable tc ruff check (#752)

    • More safe types (#741)

    • Should be safe types (#740)

    • Type only session (#742)

    • Types for Application (#735)

    • Typing for commands (#738)

    • Type examples too (#748)

    • Typing fixes from Ruff (#749)

    • Minor typing updates (#780)

  • New daemon implementation avoiding fork in certain code paths to improve portability (#783)
    • Make the daemon launcher private to avoid exposing internals (#794)

  • Application instances now provide bash and fish completions out of the box (#782)

  • Drop use of atexit for color handling to avoid teardown issues (#785)

Removed:

  • Removed the long-deprecated path aliases .basename, .isdir(), .isfile(), and .islink(); use .name, .is_dir(), .is_file(), and .is_symlink() instead.

  • Removed the deprecated SshMachine.nohup() method; use the command .nohup modifier or daemonic_popen() instead.

Deprecated:

  • Style.print_ (a Python 2 era shortcut) now emits a FutureWarning; use .print instead.

Fixes:

  • HTMLStyle should respect the color setting (#776)

  • Make cache clear abstract (#747)

  • Use perf_counter instead of datetime for Progress (#754)

  • BrokenPipeError when piping CLI help output to head (#727)

  • Fill out all and dir for modules to improve introspection (#793)

  • Fix quoting inconsistency between standalone commands and pipelines (#792)

  • Only register shutdown if a timeout was requested to avoid spurious handlers (#784)

  • Reduce various warnings reported by linters and at runtime (#781)

  • Fix O(n^2) performance in StdinDataRedirection.popen (#798)

  • Correct async remote streaming, pipeline TEE, and command binding (#806)

  • Fix a sweep of pre-2.0 bugs across cli, paths, and commands (#808)

  • Fix assorted pre-2.0 correctness issues, including stem, the SSH tunnel timeout, and Set completion (#809)

  • Make ** glob recursively like pathlib (#628, #812)

  • Fix SSH drive-letter path handling on Windows (#762)

Tests:

  • Need ImportError for plumbum.cmd (#769)

  • Set LANG=C in pytest to fix locale-dependent test failures (#772)

  • Support pytest 9 (#757)

  • pytest log_level is better than log_cli_level (#756)

  • Increased test coverage and related test improvements (#789)

Internal:

  • Adding more slots (#750)

  • Even more slots (#751)

  • tid imports (#753)

  • Fix badge showing wrong branch (#736)

  • Fix links in changelog (#732)

  • Add AGENTS.md and avoid extra files (#803)

  • Remove dead code and simplify a few spots (#811)

  • Tighten loose type annotations across the public API (#810)

  • Make module import lazy (#814)

Docs:

  • Converted docs and changelog formatting, moved to Furo theme and fixed local build issues and links (#787, #786)

  • Fixed various documentation warnings and added missing API docs (#791)

  • Filled out older NEWS/changes entries and added a couple of missing docs files (#790, #788)

CI:

  • Test on prerelease of 3.15 (#771)

  • Update versions (#763)

  • Updating ssh script (#764)

  • Use PyPy 3.11 (#770)

  • Bump setup-uv to maintained tag scheme (#799)

1.10.0

  • Add Python π, drop Python 3.8 (#705)

  • CLI: Fix flag invoke behavior to respect False values (#724)

  • CLI: Allow subapplication registration under multiple subcommand names (#725)

  • CLI: Better support for future annotations on 3.9 (#723)

  • CLI: Termsize determination fixed on Linux Python 3.14, broken due to wrong struct size (#721)

  • Drop some unused variables (#700)

  • Simpler urljoin (#707)

  • Use SPDX license identifier (#702)

1.9.0

  • Drop Python <3.8 (#683)

  • Add 3.13 testing and classifiers (#690)

  • setuptools-less installs couldn’t translate, add test (#373)

  • Use importlib resources (#680)

  • Replaced NotImplementedError by NotImplemented (#667)

1.8.3

  • Fix StdinDataRedirection’s formulate() method (#629)

  • Missing non-zero return code from TEE’d pipe (661)

  • Fix stalling in Pipeline command (#632)

  • Use high-speed method for C locale too (#691)

  • Update index.rst - fix broken link for sh project (#658)

1.8.2

  • Fix author metadata on PyPI package and add static check (#648)

  • Add testing for Python 3.12 beta 1 (#650)

  • Use Ruff for linting (#643)

  • Paths: Add type hinting for Path (#646)

1.8.1

  • Accept path-like objects (#627)

  • Move the build backend to hatchling and hatch-vcs. Users should be unaffected. Third-party packaging may need to adapt to the new build system. (#607)

1.8.0

  • Drop Python 2.7 and 3.5 support, add 3.11 support (#573)

  • Lots of extended checks and fixes for problems exposed.

  • Color: support NO_COLOR/FORCE_COLOR (#575)

  • Commands: New iter_lines buffer_size parameter (#582)

  • Commands: cache remote commands (#583)

  • SSH: Support reverse tunnels and dynamically allocated ports (#608)

  • CLI: add Set(..., all_markers={"*", "all"}) and fix support for other separators (#619)

  • CLI: support future annotations (#621)

  • Color: fix the ABC (#617)

  • Exceptions: fix for exception pickling (#586)

  • Fix for StdinDataRedirection and modifiers (#605)

1.7.2

  • Commands: avoid issue mktemp issue on some BSD variants (#571)

  • Better specification of dependency on pywin32 (#568)

  • Some DeprecationWarnings changed to FutureWarnings (#567)

1.7.1

  • Paths: glob with local paths no longer expands the existing path too (#552)

  • Paramiko: support reverse tunnels (#562)

  • SSHMachine: support forwarding Unix sockets in .tunnel() (#550)

  • CLI: Support COLOR_GROUP_TITLES (#553)

  • Fix a deprecated in Python 3.10 warning (#563)

  • Extended testing and checking on Python 3.10 and various PyPy versions. Nox is supported for easier new-user development.

1.7.0

  • Commands: support .with_cwd() (#513)

  • Commands: make iter_lines deal with decoding errors during iteration (#525)

  • Commands: fix handling of env-vars passed to plumbum BoundEnvCommands (#513)

  • Commands: fix support for win32 in iter_lines (#500)

  • Paths: fix incorrect __getitem__ method in Path (#506)

  • Paths: Remote path stat had odd OSError (#505)

  • Paths: Fix RemotePath.copy() (#527)

  • Paths: missing __fspath__ added (#498)

  • SSH: better error reporting on SshSession error (#515)

  • Internal: redesigned CI, major cleanup to setuptools distribution, Black formatting, style checking throughout.

1.6.9

  • Last version to support Python 2.6; added python_requires for future versions (#507)

  • Paths: Fix bug with subscription operations (#498), (#506)

  • Paths: Fix resolve (#492)

  • Commands: Fix resolve (#491)

  • Commands: Add context manager on popen (#495)

  • Several smaller fixes (#500), (#505)

1.6.8

  • Exceptions: Changed ProcessExecutionError’s formatting to be more user-friendly (#456)

  • Commands: support for per-line timeout with iter_lines (#454)

  • Commands: support for piping stdout/stderr to a logger (#454)

  • Paths: support composing paths using subscription operations (#455)

  • CLI: Improved ‘Set’ validator to allow non-string types, and CSV params (#452)

  • TypedEnv: Facility for modeling environment-variables into python data types (#451)

  • Commands: execute local/remote commands via a magic .cmd attribute (#450)

1.6.7

  • Commands: Added run_* methods as an alternative to modifiers (#386)

  • CLI: Added support for ALLOW_ABREV (#401)

  • CLI: Added DESCRIPTION_MORE, preserves spacing (#378)

  • Color: Avoid throwing error in atexit in special cases (like pytest) (#393)

  • Including Python 3.7 in testing matrix.

  • Smaller bugfixes and other testing improvements.

1.6.6

  • Critical Bugfix: High-speed (English) translations could break the CLI module (#371)

  • Small improvement to wheels packaging

1.6.5

  • Critical Bugfix: Syntax error in image script could break pip installs (#366)

  • CLI: Regression fix: English apps now load as fast as in 1.6.3 (#364)

  • CLI: Missing colon restored in group names

  • Regression fix: Restored non-setuptools installs (but really, why would you not have setuptools?) (#367)

1.6.4

  • CLI: Support for localization (#339), with:

    • Russian by Pavel Pletenev (#339) 🇷🇺

    • Dutch by Roel Aaij (#351) 🇳🇱

    • French by Joel Closier (#352) 🇫🇷

    • German by Christoph Hasse (#353) 🇩🇪

    • Pulls with more languages welcome!

  • CLI: Support for MakeDirectory (#339)

  • Commands: Fixed unicode input/output on Python 2 (#341)

  • Paths: More updates for pathlib compatibility (#325)

  • Terminal: Changed prompt()’s default value for type parameter from int to str to match existing docs (#327)

  • Remote: Support ~ in PATH for a remote (#293)

  • Remote: Fixes for globbing with spaces in filename on a remote server (#322)

  • Color: Fixes to image plots, better separation (#324)

  • Python 3.3 has been removed from Travis and Appveyor.

  • Several bugs fixed

1.6.3

  • Python 3.6 is now supported, critical bug fixed (#302)

  • Commands: Better handling of return codes for pipelines (#288)

  • Paths: Return split support (regression) (#286) - also supports dummy args for better str compatibility

  • Paths: Added support for Python 3.6 path protocol

  • Paths: Support Python’s in syntax

  • CLI: Added Config parser (provisional) (#304)

  • Color: image plots with python -m plumbum.cli.image (#304)

  • SSH: No longer hangs for timeout seconds on failure (#306)

  • Test improvements, especially on non-linux systems

1.6.2

  • CLI: Progress now has a clear keyword that hides the bar on completion

  • CLI: Progress without clear now starts on next line without having to manually add \n.

  • Commands: modifiers now accept a timeout parameter (#281)

  • Commands: BG modifier now allows stdout/stderr redirection (to screen, for example) (#258)

  • Commands: Modifiers no longer crash on repr (see #262)

  • Remote: nohup works again, typo fixed (#261)

  • Added better support for SunOS and other OS’s. (#260)

  • Colors: Context manager flushes stream now, provides more consistent results

  • Other smaller bugfixes, better support for Python 3.6+

1.6.1

  • CLI: Application subclasses can now be run directly, instead of calling .run(), to facilitate using as entry points (#237)

  • CLI: gui_open added to allow easy opening of paths in default gui editor (#239)

  • CLI: More control over help message (#233)

  • Remote: cwd is now stashed to reduce network usage (similar to Plumbum <1.6 behavior), and absolute paths are faster, (#238)

  • Bugfix: Pipelined return codes now give correct attribution (#243)

  • Bugfix: Progress works on Python 2.6 (#230)

  • Bugfix: Colors now work with more terminals (#231)

  • Bugfix: Getting an executable no longer returns a directory (#234)

  • Bugfix: Iterdir now works on Python <3.5

  • Testing is now expanded and fully written in Pytest, with coverage reporting.

  • Added support for Conda ( as of 1.6.2, use the -c conda-forge channel)

1.6.0

  • Added support for Python 3.5, PyPy, and better Windows and Mac support, with CI testing (#218, #217, #226)

  • Colors: Added colors module, support for colors added to cli (#213)

  • Machines: Added .get() method for checking several commands. (#205)

  • Machines: local.cwd now is the current directory even if you change the directory with non-Plumbum methods (fixes unexpected behavior). (#207)

  • SSHMachine: Better error message for SSH (#211)

  • SSHMachine: Support for FreeBSD remote (#220)

  • Paths: Now a subclass of str, can be opened directly (#228)

  • Paths: Improved pathlib compatibility with several additions and renames (#223)

  • Paths: Added globbing multiple patterns at once (#221)

  • Commands: added NOHUP modifier (#221)

  • CLI: added positional argument validation (#225)

  • CLI: added envname, which allows you specify an environment variable for a SwitchAttr (#216)

  • CLI terminal: added Progress, a command line progress bar for iterators and ranges (#214)

  • Continued to clean out Python 2.5 hacks

1.5.0

  • Removed support for Python 2.5. (Travis CI does not support it anymore)

  • CLI: add invoke, which allows you to programmatically run applications (#149)

  • CLI: add --help-all and various cosmetic fixes: (#125), (#126), (#127)

  • CLI: add root_app property (#141)

  • Machines: getattr now raises AttributeError instead of CommandNotFound (#135)

  • Paramiko: keep_alive support (#186)

  • Paramiko: does not support piping explicitly now (#160)

  • Parmaiko: Added pure SFTP backend, gives STFP v4+ support (#188)

  • Paths: bugfix to cwd interaction with Path (#142)

  • Paths: read/write now accept an optional encoding parameter (#148)

  • Paths: Suffix support similar to the Python 3.4 standard library pathlib (#198)

  • Commands: renamed setenv to with_env (#143)

  • Commands: pipelines will now fail with ProcessExecutionError if the source process fails (#145)

  • Commands: added TF and RETCODE modifiers (#202)

  • Experimental concurrent machine support in experimental/parallel.py

  • Several minor bug fixes, including Windows and Python 3 fixes (#199, #195)

1.4.2

  • Paramiko now supports Python 3, enabled support in Plumbum

  • Terminal: added prompt(), bugfix to get_terminal_size() (#113)

  • CLI: added cleanup(), which is called after main() returns

  • CLI: bugfix to CountOf (#118)

  • Commands: Add a TEE modifier (#117)

  • Remote machines: bugfix to which, bugfix to remote environment variables (#122)

  • Path: read()/write() now operate on bytes

1.4.1

  • Force /bin/sh to be the shell in SshMachine.session() (#111)

  • Added islink() and unlink() to path objects (#100, #103)

  • Added access to path objects

  • Faster which implementation (#98)

  • Several minor bug fixes

1.4

  • Moved atomic and unixutils into the new fs package (file-system related utilities)

  • Dropped plumbum.utils legacy shortcut in favor of plumbum.path.utils

  • Bugfix: the left-hand-side process of a pipe wasn’t waited on, leading to zombies (#89)

  • Added RelativePath (the result of Path.relative_to)

  • Fixed more text alignment issues in cli.Application.help()

  • Introduced ask() and choose to cli.terminal

  • Bugfix: Path comparison operators were wrong

  • Added connection timeout to RemoteMachine

1.3

  • Command.popen: a new argument, new_session may be passed to Command.popen, which runs the given in a new session (setsid on POSIX, CREATE_NEW_PROCESS_GROUP on Windows)

  • Command.Popen: args can now also be a list (previously, it was required to be a tuple). See

  • local.daemonize: run commands as full daemons (double-fork and setsid) on POSIX systems, or detached from their controlling console and parent (on Windows).

  • list_processes: return a list of running process (local/remote machines)

  • SshMachine.nohup: “daemonize” remote commands via nohup (not really a daemon, but good enough)

  • atomic: Atomic file operations (AtomicFile, AtomicCounterFile and PidFile)

  • copy and move: the src argument can now be a list of files to move, e.g., copy(["foo", "bar"], "/usr/bin")

  • list local and remote processes

  • cli: better handling of text wrapping in the generated help message

  • cli: add a default main() method that checks for unknown subcommands

  • terminal: initial commit (get_terminal_size)

  • packaging: the package was split into subpackages; it grew too big for a flat namespace. imports are not expected to be broken by this change

  • SshMachine: added password parameter, which relies on sshpass to feed the password to ssh. This is a security risk, but it’s occasionally necessary. Use this with caution!

  • Commands now have a machine attribute that points to the machine they run on

  • Commands gained setenv, which creates a command with a bound environment

  • Remote path: several fixes to stat (StatRes)

  • cli: add lazily-loaded subcommands (e.g., MainApp.subcommand("foo", "my.package.foo.FooApp")), which are imported on demand

  • Paths: added relative_to and split, which (respectively) computes the difference between two paths and splits paths into lists of nodes

  • cli: Predicate became a class decorator (it exists solely for pretty-printing anyway)

  • PuttyMachine: bugfix

1.2

1.1

  • Paramiko integration (#10)

  • CLI: now with built-in support for sub-commands. See also: #43

  • The “import hack” has moved to the package’s __init__.py, to make it importable directly (#45)

  • Paths now support chmod (on POSIX platform) (#49)

  • The argument name of a SwitchAttr can now be given to it (defaults to VALUE) (#46)

1.0.1

  • Windows: path are no longer converted to lower-case, but __eq__ and __hash__ operate on the lower-cased result (#38)

  • Properly handle empty strings in the argument list (#41)

  • Relaxed type-checking of LocalPath and RemotePath (#35)

  • Added PuttyMachine for Windows users that relies on plink and pscp (instead of ssh and scp) (#37)

1.0.0

  • Rename cli.CountingAttr to cli.CountOf

  • Moved to Travis continuous integration

  • Added unixutils

  • Added chown and uid/gid

  • Lots of fixes and updates to the doc

  • Full list of issues

0.9.0

Initial release