Skip to content

fix: make bash completion script work without bash-completion pkg#3801

Merged
frostming merged 1 commit into
pdm-project:mainfrom
mokashang:fix/bash-completion-without-bash-completion-pkg
Jun 3, 2026
Merged

fix: make bash completion script work without bash-completion pkg#3801
frostming merged 1 commit into
pdm-project:mainfrom
mokashang:fix/bash-completion-without-bash-completion-pkg

Conversation

@mokashang

Copy link
Copy Markdown
Contributor

Pull Request Checklist

  • A news fragment is added in news/ describing what is new.
  • Test cases added for changed code.

Describe what you have changed in this PR.

Fixes #3793.

The bug

pdm completion bash's output assumes the bash-completion package is loaded in the user's shell. It calls two helpers from that package — _get_comp_words_by_ref and __ltrim_colon_completions — without checking whether they exist. When they don't (Git Bash on Windows is the common case, but it also affects minimal Linux containers without bash-completion installed), bash prints

bash: _get_comp_words_by_ref: command not found
…
bash: __ltrim_colon_completions: command not found

in the middle of every Tab-completion the user triggers, exactly as reported in #3793.

The fix

Define small fallback implementations of both helpers at the top of pdm.bash, guarded with declare -F … so the real implementations from bash-completion take precedence whenever they're available.

The fallbacks intentionally cover only what the rest of the script needs:

  • _get_comp_words_by_ref accepts the same option shape (-n <chars>, -c, -w, etc.) without honoring it, then populates cur / prev / words / cword straight from $COMP_WORDS. pdm subcommand and option names contain no word-break characters, so this output matches the real helper for every invocation in this script.
  • __ltrim_colon_completions becomes a no-op for the same reason — there are no colons in pdm command/option names to trim.

This keeps the patch small (~35 lines of bash) and avoids any new runtime dependency. The bash syntax was checked with bash -n and the file behaves the same as before when bash-completion is loaded.

Tests

Added test_completion_bash_runs_without_bash_completion_pkg in tests/cli/test_completion.py. It:

  1. Sources the generated pdm.bash in a fresh bash subprocess with _get_comp_words_by_ref and __ltrim_colon_completions explicitly cleared (so the test is deterministic even on hosts that already have bash-completion installed).
  2. Invokes the completion function for pdm <TAB>.
  3. Asserts no command not found reaches stderr and that the expected subcommand candidates (add, install, venv) still appear on stdout.

Verified that the test fails on main (it reproduces both command not found errors) and passes with this change. The test is skipped on Windows and when bash is not on $PATH.

Full tests/cli/test_completion.py suite: 9 passed locally on macOS / Python 3.13.

Notes

The file header still says # Generated by pycomplete 0.4.0. pycomplete itself hasn't seen activity since 2023 and the dev-dep is pinned to ~=0.3, so the bash template upstream wouldn't be regenerated through it on the next release. Patching the checked-in file directly is the lowest-risk fix; the same fallback pattern can be lifted into pycomplete/templates/bash.tpl later if a regeneration is ever done.

The generated bash completion script for pdm calls two helpers from
the `bash-completion` package (`_get_comp_words_by_ref` and
`__ltrim_colon_completions`). When those helpers are not loaded —
e.g. Git Bash on Windows or a minimal Linux container — bash prints
`command not found` for each missing helper in the middle of the
candidate list every time the user presses Tab.

Define small fallback implementations at the top of the script,
guarded with `declare -F` so the real helpers from `bash-completion`
take precedence when present. The fallbacks intentionally cover only
what this script needs: pdm subcommands and option names contain no
word-break characters, so `_get_comp_words_by_ref -n :` reduces to
"read the current word and $COMP_WORDS" and
`__ltrim_colon_completions` becomes a no-op.

A regression test sources the script in a clean bash subprocess with
the helpers explicitly cleared, invokes the completion function for
`pdm <TAB>`, and asserts no `command not found` reaches stderr while
the expected subcommand candidates still appear on stdout.

Fixes pdm-project#3793
@codecov

codecov Bot commented Jun 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.29%. Comparing base (09c95b7) to head (93d384a).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3801   +/-   ##
=======================================
  Coverage   86.29%   86.29%           
=======================================
  Files         118      118           
  Lines       12536    12536           
  Branches     2093     2093           
=======================================
  Hits        10818    10818           
  Misses       1147     1147           
  Partials      571      571           
Flag Coverage Δ
unittests 86.17% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@frostming frostming merged commit 935b256 into pdm-project:main Jun 3, 2026
21 of 22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pdm completions prints "bash: __ltrim_colon_completions: command not found" in the middle of completions

2 participants