[{"data":1,"prerenderedAt":1123},["ShallowReactive",2],{"page-\u002Fadvanced-input-parsing-user-experience\u002Fshell-completion-for-python-clis\u002Finstalling-shell-completion-for-bash-zsh-fish\u002F":3,"content-directory":877},{"id":4,"title":5,"body":6,"date":863,"description":864,"difficulty":865,"draft":866,"extension":867,"meta":868,"navigation":869,"path":870,"seo":871,"stem":872,"tags":873,"updated":863,"__hash__":876},"content\u002Fadvanced-input-parsing-user-experience\u002Fshell-completion-for-python-clis\u002Finstalling-shell-completion-for-bash-zsh-fish\u002Findex.md","Installing Shell Completion for bash, zsh, fish",{"type":7,"value":8,"toc":851},"minimark",[9,19,24,127,131,138,142,196,211,241,247,271,273,283,286,299,319,336,345,376,383,385,394,423,467,491,493,496,525,534,541,544,583,589,593,596,599,705,739,745,749,813,817,847],[10,11,12,13,18],"p",{},"You have completion working in Python — subcommands and options complete when you drive the protocol by hand. This guide is the other half: getting that completion to fire automatically in a real shell. Each shell loads completion scripts from its own place and in its own way, so \"install completion\" means three different recipes for bash, zsh, and fish. This walks through generating the script and placing it for each shell, choosing between eager and sourced loading, and fixing the classic failure where Tab does nothing at all. Enabling completion in your code is the ",[14,15,17],"a",{"href":16},"\u002Fadvanced-input-parsing-user-experience\u002Fshell-completion-for-python-clis\u002Fenabling-tab-completion-in-click-and-typer\u002F","sibling guide","; here we assume that already works and focus on the shell.",[20,21,23],"h2",{"id":22},"tldr","TL;DR",[25,26,27,60,77,95,109],"ul",{},[28,29,30,34,35,39,40,43,44,47,48,51,52,55,56,59],"li",{},[31,32,33],"strong",{},"Generate the script."," Click: ",[36,37,38],"code",{},"_YOURCLI_COMPLETE=bash_source yourcli"," (swap ",[36,41,42],{},"bash","→",[36,45,46],{},"zsh","\u002F",[36,49,50],{},"fish","). Typer: ",[36,53,54],{},"yourcli --show-completion",", or let ",[36,57,58],{},"yourcli --install-completion"," do it for you.",[28,61,62,65,66,69,70,73,74,76],{},[31,63,64],{},"bash:"," source the script from ",[36,67,68],{},"~\u002F.bashrc",", or drop it in a ",[36,71,72],{},"bash-completion"," directory. Requires the ",[36,75,72],{}," package.",[28,78,79,82,83,86,87,90,91,94],{},[31,80,81],{},"zsh:"," the script must be on ",[36,84,85],{},"fpath"," and ",[36,88,89],{},"compinit"," must run — set both in ",[36,92,93],{},"~\u002F.zshrc",".",[28,96,97,100,101,104,105,108],{},[31,98,99],{},"fish:"," put a file named ",[36,102,103],{},"yourcli.fish"," in ",[36,106,107],{},"~\u002F.config\u002Ffish\u002Fcompletions\u002F","; fish autoloads it, no sourcing needed.",[28,110,111,114,115,118,119,122,123,126],{},[31,112,113],{},"Nothing happens?"," Start a ",[31,116,117],{},"fresh shell",", check the command is on ",[36,120,121],{},"PATH",", and in zsh run ",[36,124,125],{},"rehash",". Ninety percent of \"completion is broken\" is one of these.",[20,128,130],{"id":129},"generate-the-completion-script","Generate the completion script",[10,132,133,134,137],{},"Every install starts by generating the shell script your CLI already knows how to emit. With Click you set the completion trigger to the ",[36,135,136],{},"*_source"," variant for the target shell and capture stdout:",[139,140],"inline-diagram",{"name":141},"completion-script-flow",[143,144,148],"pre",{"className":145,"code":146,"language":42,"meta":147,"style":147},"language-bash shiki shiki-themes github-light github-dark","$ _YOURCLI_COMPLETE=bash_source yourcli   # prints the bash completion script\n$ _YOURCLI_COMPLETE=zsh_source yourcli    # zsh version\n$ _YOURCLI_COMPLETE=fish_source yourcli   # fish version\n","",[36,149,150,170,183],{"__ignoreMap":147},[151,152,155,159,163,166],"span",{"class":153,"line":154},"line",1,[151,156,158],{"class":157},"sScJk","$",[151,160,162],{"class":161},"sZZnC"," _YOURCLI_COMPLETE=bash_source",[151,164,165],{"class":161}," yourcli",[151,167,169],{"class":168},"sJ8bj","   # prints the bash completion script\n",[151,171,173,175,178,180],{"class":153,"line":172},2,[151,174,158],{"class":157},[151,176,177],{"class":161}," _YOURCLI_COMPLETE=zsh_source",[151,179,165],{"class":161},[151,181,182],{"class":168},"    # zsh version\n",[151,184,186,188,191,193],{"class":153,"line":185},3,[151,187,158],{"class":157},[151,189,190],{"class":161}," _YOURCLI_COMPLETE=fish_source",[151,192,165],{"class":161},[151,194,195],{"class":168},"   # fish version\n",[10,197,198,199,202,203,206,207,210],{},"The variable name is derived from your console-script name: uppercased, hyphens to underscores, ",[36,200,201],{},"_COMPLETE"," appended (",[36,204,205],{},"my-tool"," → ",[36,208,209],{},"_MY_TOOL_COMPLETE","). With Typer you do not need to remember the variable — the app prints the script for you:",[143,212,214],{"className":145,"code":213,"language":42,"meta":147,"style":147},"$ yourcli --show-completion    # prints the script for your current shell\n$ yourcli --install-completion # generates AND installs it in the right place\n",[36,215,216,229],{"__ignoreMap":147},[151,217,218,220,222,226],{"class":153,"line":154},[151,219,158],{"class":157},[151,221,165],{"class":161},[151,223,225],{"class":224},"sj4cs"," --show-completion",[151,227,228],{"class":168},"    # prints the script for your current shell\n",[151,230,231,233,235,238],{"class":153,"line":172},[151,232,158],{"class":157},[151,234,165],{"class":161},[151,236,237],{"class":224}," --install-completion",[151,239,240],{"class":168}," # generates AND installs it in the right place\n",[10,242,243,246],{},[36,244,245],{},"--install-completion"," is the fast path for end users; the manual placement below is what you reach for when you want the script checked into a package, deployed system-wide, or loaded a particular way.",[10,248,249,250,253,254,257,258,262,263,266,267,270],{},"Confirm which shell you are actually in before you generate — the login shell in your terminal is not always what you assume. ",[36,251,252],{},"echo \"$0\""," or ",[36,255,256],{},"ps -p $$ -o comm="," prints the running shell, and you want the script for ",[259,260,261],"em",{},"that"," shell, not for whatever ",[36,264,265],{},"chsh"," says your default is. Generating a zsh script and sourcing it from a bash ",[36,268,269],{},".bashrc"," is a surprisingly common way to get silent, do-nothing completion.",[20,272,42],{"id":42},[10,274,275,276,278,279,282],{},"bash completion relies on the ",[36,277,72],{}," package (preinstalled on most Linux distributions; ",[36,280,281],{},"brew install bash-completion@2"," on macOS). There are two good placements.",[139,284],{"name":285},"completion-install-matrix",[10,287,288,294,295,298],{},[31,289,290,291,293],{},"Sourced from ",[36,292,269],{}," (simplest, per-user)."," Generate the script once into a file and source it on shell start. Generating to a file rather than ",[36,296,297],{},"eval","-ing on every startup keeps your shell fast — you are not launching Python each time a shell opens.",[143,300,302],{"className":145,"code":301,"language":42,"meta":147,"style":147},"$ _YOURCLI_COMPLETE=bash_source yourcli > ~\u002F.yourcli-complete.bash\n",[36,303,304],{"__ignoreMap":147},[151,305,306,308,310,312,316],{"class":153,"line":154},[151,307,158],{"class":157},[151,309,162],{"class":161},[151,311,165],{"class":161},[151,313,315],{"class":314},"szBVR"," >",[151,317,318],{"class":161}," ~\u002F.yourcli-complete.bash\n",[143,320,322],{"className":145,"code":321,"language":42,"meta":147,"style":147},"# ~\u002F.bashrc\nsource ~\u002F.yourcli-complete.bash\n",[36,323,324,329],{"__ignoreMap":147},[151,325,326],{"class":153,"line":154},[151,327,328],{"class":168},"# ~\u002F.bashrc\n",[151,330,331,334],{"class":153,"line":172},[151,332,333],{"class":224},"source",[151,335,318],{"class":161},[10,337,338,341,342,344],{},[31,339,340],{},"Dropped in a completion directory (per-user or system)."," bash-completion autoloads files from its user directory, so no ",[36,343,333],{}," line is needed:",[143,346,348],{"className":145,"code":347,"language":42,"meta":147,"style":147},"$ mkdir -p ~\u002F.local\u002Fshare\u002Fbash-completion\u002Fcompletions\n$ _YOURCLI_COMPLETE=bash_source yourcli > ~\u002F.local\u002Fshare\u002Fbash-completion\u002Fcompletions\u002Fyourcli\n",[36,349,350,363],{"__ignoreMap":147},[151,351,352,354,357,360],{"class":153,"line":154},[151,353,158],{"class":157},[151,355,356],{"class":161}," mkdir",[151,358,359],{"class":224}," -p",[151,361,362],{"class":161}," ~\u002F.local\u002Fshare\u002Fbash-completion\u002Fcompletions\n",[151,364,365,367,369,371,373],{"class":153,"line":172},[151,366,158],{"class":157},[151,368,162],{"class":161},[151,370,165],{"class":161},[151,372,315],{"class":314},[151,374,375],{"class":161}," ~\u002F.local\u002Fshare\u002Fbash-completion\u002Fcompletions\u002Fyourcli\n",[10,377,378,379,382],{},"For a system-wide install (a package's postinstall step, say), write to ",[36,380,381],{},"\u002Fusr\u002Fshare\u002Fbash-completion\u002Fcompletions\u002Fyourcli"," instead. Either way, open a new shell to pick it up.",[20,384,46],{"id":46},[10,386,387,388,390,391,393],{},"zsh needs two things to be true: your completion script must be on ",[36,389,85],{},", and the completion system (",[36,392,89],{},") must have been initialised. Miss either and Tab stays silent. The robust per-user setup:",[143,395,397],{"className":145,"code":396,"language":42,"meta":147,"style":147},"$ mkdir -p ~\u002F.zfunc\n$ _YOURCLI_COMPLETE=zsh_source yourcli > ~\u002F.zfunc\u002F_yourcli\n",[36,398,399,410],{"__ignoreMap":147},[151,400,401,403,405,407],{"class":153,"line":154},[151,402,158],{"class":157},[151,404,356],{"class":161},[151,406,359],{"class":224},[151,408,409],{"class":161}," ~\u002F.zfunc\n",[151,411,412,414,416,418,420],{"class":153,"line":172},[151,413,158],{"class":157},[151,415,177],{"class":161},[151,417,165],{"class":161},[151,419,315],{"class":314},[151,421,422],{"class":161}," ~\u002F.zfunc\u002F_yourcli\n",[143,424,426],{"className":145,"code":425,"language":42,"meta":147,"style":147},"# ~\u002F.zshrc  — order matters: fpath BEFORE compinit\nfpath=(~\u002F.zfunc $fpath)\nautoload -Uz compinit && compinit\n",[36,427,428,433,450],{"__ignoreMap":147},[151,429,430],{"class":153,"line":154},[151,431,432],{"class":168},"# ~\u002F.zshrc  — order matters: fpath BEFORE compinit\n",[151,434,435,438,441,444,447],{"class":153,"line":172},[151,436,85],{"class":437},"sVt8B",[151,439,440],{"class":314},"=",[151,442,443],{"class":437},"(",[151,445,446],{"class":161},"~\u002F.zfunc",[151,448,449],{"class":437}," $fpath)\n",[151,451,452,455,458,461,464],{"class":153,"line":185},[151,453,454],{"class":224},"autoload",[151,456,457],{"class":224}," -Uz",[151,459,460],{"class":161}," compinit",[151,462,463],{"class":437}," && ",[151,465,466],{"class":157},"compinit\n",[10,468,469,470,473,474,476,477,480,481,483,484,487,488,94],{},"The leading underscore in the filename (",[36,471,472],{},"_yourcli",") is a zsh convention for completion functions — keep it. If you use a framework like Oh My Zsh that already calls ",[36,475,89],{},", just make sure your ",[36,478,479],{},"fpath="," line runs before it; putting the ",[36,482,85],{}," addition near the top of ",[36,485,486],{},".zshrc"," is the safe choice. After editing, start a new shell or run ",[36,489,490],{},"exec zsh",[20,492,50],{"id":50},[10,494,495],{},"fish is the easiest of the three: it autoloads any file in its completions directory, matched by command name. No sourcing, no init call.",[143,497,499],{"className":145,"code":498,"language":42,"meta":147,"style":147},"$ mkdir -p ~\u002F.config\u002Ffish\u002Fcompletions\n$ _YOURCLI_COMPLETE=fish_source yourcli > ~\u002F.config\u002Ffish\u002Fcompletions\u002Fyourcli.fish\n",[36,500,501,512],{"__ignoreMap":147},[151,502,503,505,507,509],{"class":153,"line":154},[151,504,158],{"class":157},[151,506,356],{"class":161},[151,508,359],{"class":224},[151,510,511],{"class":161}," ~\u002F.config\u002Ffish\u002Fcompletions\n",[151,513,514,516,518,520,522],{"class":153,"line":172},[151,515,158],{"class":157},[151,517,190],{"class":161},[151,519,165],{"class":161},[151,521,315],{"class":314},[151,523,524],{"class":161}," ~\u002F.config\u002Ffish\u002Fcompletions\u002Fyourcli.fish\n",[10,526,527,528,530,531,94],{},"The filename must match the command (",[36,529,103],{},"). fish picks it up in new shells automatically, and often in the current one too because it loads completions lazily on first use. For a system-wide install, write to ",[36,532,533],{},"\u002Fusr\u002Fshare\u002Ffish\u002Fvendor_completions.d\u002Fyourcli.fish",[20,535,537,538,540],{"id":536},"eager-eval-vs-sourced-from-file","Eager (",[36,539,297],{},") vs sourced-from-file",[10,542,543],{},"You will see two styles in the wild and it is worth knowing the trade-off.",[25,545,546,570],{},[28,547,548,554,555,558,559,561,562,565,566,94],{},[31,549,550,551,553],{},"Eager \u002F ",[36,552,297],{}," on every startup"," — ",[36,556,557],{},"eval \"$(_YOURCLI_COMPLETE=bash_source yourcli)\""," directly in ",[36,560,269],{},". Always current, because it regenerates the script each time. The cost: it launches your Python program on ",[31,563,564],{},"every"," new shell, which adds startup latency you will feel if the tool is heavy. This is where a fast CLI pays off — see ",[14,567,569],{"href":568},"\u002Fmodern-python-cli-frameworks-architecture\u002Fcli-startup-performance-and-lazy-loading\u002F","CLI startup performance and lazy loading",[28,571,572,575,576,578,579,582],{},[31,573,574],{},"Sourced from a generated file"," — generate once to a file, then ",[36,577,333],{}," or autoload that file. Zero Python at shell start, so it is the better default. The catch: the script is a snapshot. If you add subcommands or rename options, the ",[259,580,581],{},"shape"," of completion can go stale until you regenerate. (Dynamic value callbacks still run your program at Tab-time, so those stay fresh regardless.)",[10,584,585,586,588],{},"For most users, generate to a file. Reserve ",[36,587,297],{}," for a machine where you are actively developing the CLI and want the completion structure to track your code without regenerating.",[20,590,592],{"id":591},"troubleshooting-completion-never-fires","Troubleshooting: completion never fires",[10,594,595],{},"When Tab does nothing, work down this list — the fixes are almost always mundane.",[139,597],{"name":598},"completion-troubleshoot-tree",[600,601,602,615,648,669,682,693,699],"ol",{},[28,603,604,607,608,611,612,614],{},[31,605,606],{},"You did not start a fresh shell."," Completion registration happens at shell startup. After installing, open a new terminal or run ",[36,609,610],{},"exec bash"," \u002F ",[36,613,490],{}," \u002F start a new fish. This is the single most common cause.",[28,616,617,622,623,626,627,629,630,632,633,636,637,640,641,643,644,94],{},[31,618,619,620,94],{},"The command is not on ",[36,621,121],{}," The generated script invokes ",[36,624,625],{},"yourcli"," to compute candidates. If ",[36,628,625],{}," is not on the ",[36,631,121],{}," of the shell where you press Tab, nothing comes back. Confirm with ",[36,634,635],{},"command -v yourcli",". Tools installed with pipx or ",[36,638,639],{},"uv tool"," put a launcher on ",[36,642,121],{}," for you — see ",[14,645,647],{"href":646},"\u002Fproject-setup-dependency-management\u002Fpackaging-python-clis-for-distribution\u002Finstalling-and-distributing-clis-with-pipx\u002F","installing and distributing CLIs with pipx",[28,649,650,653,654,656,657,659,660,662,663,666,667,94],{},[31,651,652],{},"zsh needs a rehash."," zsh caches the commands on ",[36,655,121],{},". Right after installing a new CLI, zsh may not \"see\" it until you run ",[36,658,125],{}," (or open a new shell). If the completion function itself is not found, re-check that ",[36,661,85],{}," is set ",[259,664,665],{},"before"," ",[36,668,89],{},[28,670,671,674,675,678,679,94],{},[31,672,673],{},"Stale zsh completion cache."," zsh caches completion metadata in ",[36,676,677],{},"~\u002F.zcompdump",". After changing a completion script, remove it and reinitialise: ",[36,680,681],{},"rm -f ~\u002F.zcompdump && compinit",[28,683,684,687,688,690,691,76],{},[31,685,686],{},"bash-completion is not installed."," The generated bash script depends on the ",[36,689,72],{}," runtime. On a minimal system it may be missing; install the ",[36,692,72],{},[28,694,695,698],{},[31,696,697],{},"It works in one shell but not another."," Each shell loads a different script from a different place, so a working bash setup tells you nothing about zsh. Install completion once per shell you actually use, and test each in its own fresh session.",[28,700,701,704],{},[31,702,703],{},"The Python side is actually broken."," Rule this out fast by driving the protocol directly, with no shell in the loop:",[143,706,708],{"className":145,"code":707,"language":42,"meta":147,"style":147},"$ _YOURCLI_COMPLETE=bash_complete COMP_WORDS=\"yourcli \" COMP_CWORD=1 yourcli\nplain,deploy\nplain,status\n",[36,709,710,729,734],{"__ignoreMap":147},[151,711,712,714,717,720,723,726],{"class":153,"line":154},[151,713,158],{"class":157},[151,715,716],{"class":161}," _YOURCLI_COMPLETE=bash_complete",[151,718,719],{"class":161}," COMP_WORDS=\"yourcli \"",[151,721,722],{"class":161}," COMP_CWORD=",[151,724,725],{"class":224},"1",[151,727,728],{"class":161}," yourcli\n",[151,730,731],{"class":153,"line":172},[151,732,733],{"class":157},"plain,deploy\n",[151,735,736],{"class":153,"line":185},[151,737,738],{"class":157},"plain,status\n",[10,740,741,742,94],{},"If that prints candidates, your program is fine and the problem is placement or loading. If it prints nothing, fix completion in your code first — that is the ",[14,743,744],{"href":16},"enabling guide",[20,746,748],{"id":747},"production-notes","Production notes",[25,750,751,760,770,790,800],{},[28,752,753,756,757,759],{},[31,754,755],{},"Ship the script, don't make users generate it."," When you package the CLI, include a generated completion file per shell and document one-line install steps, or lean on Typer's ",[36,758,245],{},". Regenerate the files as part of your release so their structure tracks the code.",[28,761,762,765,766,94],{},[31,763,764],{},"The script name must match the console-script name."," Both the completion trigger variable and the filename derive from the installed command. Renaming the entry point invalidates a shipped script; keep them in step with your ",[14,767,769],{"href":768},"\u002Fmodern-python-cli-frameworks-architecture\u002Fstructuring-multi-command-python-clis\u002Fbest-practices-for-python-cli-entry-points\u002F","entry points",[28,771,772,775,776,779,780,783,784,783,786,789],{},[31,773,774],{},"System vs user directories."," System paths (",[36,777,778],{},"\u002Fusr\u002Fshare\u002F...",") suit OS packages; user paths (",[36,781,782],{},"~\u002F.local\u002Fshare\u002F...",", ",[36,785,446],{},[36,787,788],{},"~\u002F.config\u002Ffish\u002Fcompletions",") suit pip\u002Fpipx installs where you cannot write to system locations.",[28,791,792,795,796,799],{},[31,793,794],{},"Test in a clean shell."," Completion bugs hide behind an already-configured environment. Verify in ",[36,797,798],{},"env -i bash --noprofile --norc"," (then source only your script) or a fresh container so you catch missing dependencies your own dotfiles paper over.",[28,801,802,805,806,611,809,812],{},[31,803,804],{},"Pin versions."," The generated script format is tied to the framework — pin ",[36,807,808],{},"click>=8.1",[36,810,811],{},"typer>=0.12"," and regenerate scripts when you upgrade.",[20,814,816],{"id":815},"related","Related",[25,818,819,825,832,841],{},[28,820,821,824],{},[14,822,823],{"href":16},"Enabling tab completion in Click and Typer"," — the sibling guide: make completion work in your Python code first.",[28,826,827,831],{},[14,828,830],{"href":829},"\u002Fadvanced-input-parsing-user-experience\u002Fshell-completion-for-python-clis\u002F","Shell completion for Python CLIs"," — the overview tying the Python and shell sides together.",[28,833,834,837,838,840],{},[14,835,836],{"href":646},"Installing and distributing CLIs with pipx"," — get the command onto users' ",[36,839,121],{}," so completion can invoke it.",[28,842,843,846],{},[14,844,845],{"href":768},"Best practices for Python CLI entry points"," — the console-script name the completion script keys off.",[848,849,850],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":147,"searchDepth":172,"depth":172,"links":852},[853,854,855,856,857,858,860,861,862],{"id":22,"depth":172,"text":23},{"id":129,"depth":172,"text":130},{"id":42,"depth":172,"text":42},{"id":46,"depth":172,"text":46},{"id":50,"depth":172,"text":50},{"id":536,"depth":172,"text":859},"Eager (eval) vs sourced-from-file",{"id":591,"depth":172,"text":592},{"id":747,"depth":172,"text":748},{"id":815,"depth":172,"text":816},"2026-07-05","Generate and install shell completion scripts for a Python CLI on bash, zsh, and fish, load them per-user or system-wide, and fix completions that never fire.","intermediate",false,"md",{},true,"\u002Fadvanced-input-parsing-user-experience\u002Fshell-completion-for-python-clis\u002Finstalling-shell-completion-for-bash-zsh-fish",{"title":5,"description":864},"advanced-input-parsing-user-experience\u002Fshell-completion-for-python-clis\u002Finstalling-shell-completion-for-bash-zsh-fish\u002Findex",[874,42,46,50,875],"completion","cli","UKbvXqdRJiyfj5A0KZQLfw-Y1CPWx1jusvTz6UnXTp0",[878,881,884,887,890,893,896,899,902,905,908,911,914,917,920,923,926,929,932,935,938,941,944,945,948,951,954,957,960,963,966,969,971,974,977,980,983,986,989,992,995,998,1001,1004,1006,1009,1012,1015,1018,1021,1024,1027,1030,1033,1036,1039,1042,1045,1048,1051,1054,1057,1060,1063,1066,1069,1072,1075,1078,1081,1084,1087,1090,1093,1096,1099,1102,1105,1108,1111,1114,1117,1120],{"path":879,"title":880},"\u002Fabout","About Python CLI Toolcraft",{"path":882,"title":883},"\u002Fadvanced-input-parsing-user-experience\u002Fadvanced-argument-validation-strategies","Advanced Argument Validation Strategies",{"path":885,"title":886},"\u002Fadvanced-input-parsing-user-experience\u002Fadvanced-argument-validation-strategies\u002Fparsing-nested-json-arguments-in-python-clis","Parsing Nested JSON Args in Python CLIs",{"path":888,"title":889},"\u002Fadvanced-input-parsing-user-experience\u002Fadvanced-argument-validation-strategies\u002Fvalidating-file-and-directory-paths-in-clis","Validating File and Directory Paths in CLIs",{"path":891,"title":892},"\u002Fadvanced-input-parsing-user-experience\u002Fcli-help-output-and-documentation\u002Fadding-examples-and-epilogs-to-help-output","Adding Examples and Epilogs to Help Output",{"path":894,"title":895},"\u002Fadvanced-input-parsing-user-experience\u002Fcli-help-output-and-documentation\u002Fgenerating-man-pages-and-docs-from-a-cli","Generating Man Pages and Docs from a CLI",{"path":897,"title":898},"\u002Fadvanced-input-parsing-user-experience\u002Fcli-help-output-and-documentation","CLI Help Output and Documentation",{"path":900,"title":901},"\u002Fadvanced-input-parsing-user-experience\u002Fcli-help-output-and-documentation\u002Fversioning-and-deprecating-cli-flags","Versioning and Deprecating CLI Flags",{"path":903,"title":904},"\u002Fadvanced-input-parsing-user-experience\u002Fcli-help-output-and-documentation\u002Fwriting-help-text-users-actually-read","Writing Help Text Users Actually Read",{"path":906,"title":907},"\u002Fadvanced-input-parsing-user-experience\u002Ferror-handling-and-exit-codes\u002Fchoosing-exit-codes-for-cli-tools","Choosing Exit Codes for CLI Tools",{"path":909,"title":910},"\u002Fadvanced-input-parsing-user-experience\u002Ferror-handling-and-exit-codes\u002Ffriendly-error-messages-and-tracebacks","Friendly Error Messages and Tracebacks",{"path":912,"title":913},"\u002Fadvanced-input-parsing-user-experience\u002Ferror-handling-and-exit-codes\u002Fhandling-keyboard-interrupt-cleanly","Handling Keyboard Interrupt Cleanly",{"path":915,"title":916},"\u002Fadvanced-input-parsing-user-experience\u002Ferror-handling-and-exit-codes","Error Handling and Exit Codes for CLIs",{"path":918,"title":919},"\u002Fadvanced-input-parsing-user-experience\u002Fhandling-configuration-files-env-vars\u002Fconfig-precedence-flags-env-files-defaults","Config Precedence: Flags, Env, Files, Defaults",{"path":921,"title":922},"\u002Fadvanced-input-parsing-user-experience\u002Fhandling-configuration-files-env-vars","Handling Config Files and Env Vars in CLIs",{"path":924,"title":925},"\u002Fadvanced-input-parsing-user-experience\u002Fhandling-configuration-files-env-vars\u002Floading-yaml-configs-safely-in-cli-apps","Loading YAML configs safely in CLI apps",{"path":927,"title":928},"\u002Fadvanced-input-parsing-user-experience","Advanced Input Parsing for Python CLIs",{"path":930,"title":931},"\u002Fadvanced-input-parsing-user-experience\u002Finteractive-terminal-ui-with-rich\u002Fadding-progress-bars-and-spinners-to-python-clis","Progress Bars and Spinners for Python CLIs",{"path":933,"title":934},"\u002Fadvanced-input-parsing-user-experience\u002Finteractive-terminal-ui-with-rich","Interactive Terminal UI with Rich",{"path":936,"title":937},"\u002Fadvanced-input-parsing-user-experience\u002Finteractive-terminal-ui-with-rich\u002Frendering-tables-and-json-with-rich","Rendering Tables and JSON with Rich",{"path":939,"title":940},"\u002Fadvanced-input-parsing-user-experience\u002Fshell-completion-for-python-clis\u002Fenabling-tab-completion-in-click-and-typer","Enabling Tab Completion in Click and Typer",{"path":942,"title":943},"\u002Fadvanced-input-parsing-user-experience\u002Fshell-completion-for-python-clis","Shell Completion for Python CLIs",{"path":870,"title":5},{"path":946,"title":947},"\u002Fadvanced-input-parsing-user-experience\u002Fstructured-logging-for-cli-apps\u002Fadding-verbose-and-quiet-logging-flags","Adding Verbose and Quiet Logging Flags",{"path":949,"title":950},"\u002Fadvanced-input-parsing-user-experience\u002Fstructured-logging-for-cli-apps","Structured Logging for CLI Apps",{"path":952,"title":953},"\u002Fadvanced-input-parsing-user-experience\u002Fstructured-logging-for-cli-apps\u002Fstructured-json-logging-in-python-clis","Structured JSON Logging in Python CLIs",{"path":955,"title":956},"\u002Fadvanced-input-parsing-user-experience\u002Fworking-with-stdin-stdout-and-pipes\u002Fdetecting-tty-and-adapting-output","Detecting a TTY and Adapting Output",{"path":958,"title":959},"\u002Fadvanced-input-parsing-user-experience\u002Fworking-with-stdin-stdout-and-pipes\u002Femitting-json-output-for-scripting","Emitting JSON Output for Scripting",{"path":961,"title":962},"\u002Fadvanced-input-parsing-user-experience\u002Fworking-with-stdin-stdout-and-pipes\u002Fhandling-broken-pipe-and-sigpipe","Handling Broken Pipe and SIGPIPE",{"path":964,"title":965},"\u002Fadvanced-input-parsing-user-experience\u002Fworking-with-stdin-stdout-and-pipes","Working with stdin, stdout and Pipes",{"path":967,"title":968},"\u002Fadvanced-input-parsing-user-experience\u002Fworking-with-stdin-stdout-and-pipes\u002Freading-piped-input-in-python-clis","Reading Piped Input in Python CLIs",{"path":47,"title":970},"Python CLI Toolcraft",{"path":972,"title":973},"\u002Fmodern-python-cli-frameworks-architecture\u002Fcli-startup-performance-and-lazy-loading","CLI Startup Performance and Lazy Loading",{"path":975,"title":976},"\u002Fmodern-python-cli-frameworks-architecture\u002Fcli-startup-performance-and-lazy-loading\u002Flazy-loading-subcommands-for-faster-startup","Lazy Loading Subcommands for Faster Startup",{"path":978,"title":979},"\u002Fmodern-python-cli-frameworks-architecture\u002Fcli-startup-performance-and-lazy-loading\u002Fprofiling-python-cli-startup-time","Profiling Python CLI Startup Time",{"path":981,"title":982},"\u002Fmodern-python-cli-frameworks-architecture\u002Fcli-startup-performance-and-lazy-loading\u002Freducing-cli-dependency-weight","Reducing CLI Dependency Weight",{"path":984,"title":985},"\u002Fmodern-python-cli-frameworks-architecture\u002Fcommand-line-parsing-with-argparse\u002Fargparse-subparsers-for-subcommands","argparse Subparsers for Subcommands",{"path":987,"title":988},"\u002Fmodern-python-cli-frameworks-architecture\u002Fcommand-line-parsing-with-argparse\u002Fargparse-vs-click-vs-typer-comparison","argparse vs Click vs Typer Compared",{"path":990,"title":991},"\u002Fmodern-python-cli-frameworks-architecture\u002Fcommand-line-parsing-with-argparse","Command-Line Parsing with argparse",{"path":993,"title":994},"\u002Fmodern-python-cli-frameworks-architecture\u002Fcommand-line-parsing-with-argparse\u002Fmigrating-from-argparse-to-typer","Migrating from argparse to Typer",{"path":996,"title":997},"\u002Fmodern-python-cli-frameworks-architecture","Python CLI Frameworks and Architecture",{"path":999,"title":1000},"\u002Fmodern-python-cli-frameworks-architecture\u002Fplugin-architectures-for-extensible-clis","Plugin Architectures for Extensible CLIs",{"path":1002,"title":1003},"\u002Fmodern-python-cli-frameworks-architecture\u002Fplugin-architectures-for-extensible-clis\u002Fwriting-a-plugin-for-an-existing-cli","Writing a Plugin for an Existing CLI",{"path":1005,"title":845},"\u002Fmodern-python-cli-frameworks-architecture\u002Fstructuring-multi-command-python-clis\u002Fbest-practices-for-python-cli-entry-points",{"path":1007,"title":1008},"\u002Fmodern-python-cli-frameworks-architecture\u002Fstructuring-multi-command-python-clis\u002Fdependency-injection-patterns-for-cli-commands","Dependency Injection Patterns for CLI Commands",{"path":1010,"title":1011},"\u002Fmodern-python-cli-frameworks-architecture\u002Fstructuring-multi-command-python-clis\u002Fhow-to-structure-a-large-python-cli-project","Structuring a Large Python CLI Project",{"path":1013,"title":1014},"\u002Fmodern-python-cli-frameworks-architecture\u002Fstructuring-multi-command-python-clis","Structuring Multi-Command Python CLIs",{"path":1016,"title":1017},"\u002Fmodern-python-cli-frameworks-architecture\u002Fstructuring-multi-command-python-clis\u002Fsharing-state-with-click-context-objects","Sharing State with Click Context Objects",{"path":1019,"title":1020},"\u002Fmodern-python-cli-frameworks-architecture\u002Ftesting-python-cli-applications","Testing Python CLI Applications",{"path":1022,"title":1023},"\u002Fmodern-python-cli-frameworks-architecture\u002Ftesting-python-cli-applications\u002Fmeasuring-cli-test-coverage","Measuring CLI Test Coverage",{"path":1025,"title":1026},"\u002Fmodern-python-cli-frameworks-architecture\u002Ftesting-python-cli-applications\u002Fmocking-filesystem-and-network-in-cli-tests","Mocking the Filesystem and Network in CLI Tests",{"path":1028,"title":1029},"\u002Fmodern-python-cli-frameworks-architecture\u002Ftesting-python-cli-applications\u002Fsnapshot-testing-cli-output","Snapshot Testing CLI Output",{"path":1031,"title":1032},"\u002Fmodern-python-cli-frameworks-architecture\u002Ftesting-python-cli-applications\u002Ftesting-click-commands-with-clirunner","Testing Click Commands with CliRunner",{"path":1034,"title":1035},"\u002Fmodern-python-cli-frameworks-architecture\u002Ftesting-python-cli-applications\u002Ftesting-interactive-prompts-and-stdin","Testing Interactive Prompts and stdin",{"path":1037,"title":1038},"\u002Fmodern-python-cli-frameworks-architecture\u002Ftyper-vs-click-when-to-use-each\u002Fbuilding-a-cli-with-subcommands-in-click","Building a CLI with subcommands in Click",{"path":1040,"title":1041},"\u002Fmodern-python-cli-frameworks-architecture\u002Ftyper-vs-click-when-to-use-each\u002Fconverting-a-click-app-to-typer","Converting a Click App to Typer",{"path":1043,"title":1044},"\u002Fmodern-python-cli-frameworks-architecture\u002Ftyper-vs-click-when-to-use-each","Typer vs Click: When to Use Each",{"path":1046,"title":1047},"\u002Fmodern-python-cli-frameworks-architecture\u002Ftyper-vs-click-when-to-use-each\u002Ftyper-callback-functions-explained","Typer callback functions explained",{"path":1049,"title":1050},"\u002Fproject-setup-dependency-management\u002Fcli-project-scaffolding-with-cookiecutter\u002Fcopier-vs-cookiecutter-for-cli-templates","Copier vs Cookiecutter for CLI Templates",{"path":1052,"title":1053},"\u002Fproject-setup-dependency-management\u002Fcli-project-scaffolding-with-cookiecutter","CLI Project Scaffolding with Cookiecutter",{"path":1055,"title":1056},"\u002Fproject-setup-dependency-management\u002Fdistributing-clis-as-standalone-binaries\u002Fbuilding-cross-platform-release-binaries-in-ci","Building Cross-Platform Release Binaries in CI",{"path":1058,"title":1059},"\u002Fproject-setup-dependency-management\u002Fdistributing-clis-as-standalone-binaries\u002Fbundling-a-python-cli-with-pyinstaller","Bundling a Python CLI with PyInstaller",{"path":1061,"title":1062},"\u002Fproject-setup-dependency-management\u002Fdistributing-clis-as-standalone-binaries\u002Fhomebrew-and-scoop-packaging-for-python-clis","Homebrew and Scoop Packaging for Python CLIs",{"path":1064,"title":1065},"\u002Fproject-setup-dependency-management\u002Fdistributing-clis-as-standalone-binaries","Distributing CLIs as Standalone Binaries",{"path":1067,"title":1068},"\u002Fproject-setup-dependency-management\u002Fdistributing-clis-as-standalone-binaries\u002Fshipping-a-cli-as-a-zipapp-with-shiv","Shipping a CLI as a Zipapp with shiv",{"path":1070,"title":1071},"\u002Fproject-setup-dependency-management","Project Setup & Dependency Management",{"path":1073,"title":1074},"\u002Fproject-setup-dependency-management\u002Fmanaging-cli-versioning-changelogs\u002Fautomating-changelogs-with-conventional-commits","Automating Changelogs with Conventional Commits",{"path":1076,"title":1077},"\u002Fproject-setup-dependency-management\u002Fmanaging-cli-versioning-changelogs\u002Fexposing-version-info-and-build-metadata","Exposing Version Info and Build Metadata",{"path":1079,"title":1080},"\u002Fproject-setup-dependency-management\u002Fmanaging-cli-versioning-changelogs","Managing CLI Versioning & Changelogs",{"path":1082,"title":1083},"\u002Fproject-setup-dependency-management\u002Fpackaging-python-clis-for-distribution\u002Fbuilding-wheels-and-sdists-for-python-clis","Building Wheels and sdists for Python CLIs",{"path":1085,"title":1086},"\u002Fproject-setup-dependency-management\u002Fpackaging-python-clis-for-distribution","Packaging Python CLIs for Distribution",{"path":1088,"title":1089},"\u002Fproject-setup-dependency-management\u002Fpackaging-python-clis-for-distribution\u002Finstalling-and-distributing-clis-with-pipx","Installing and Distributing CLIs with pipx",{"path":1091,"title":1092},"\u002Fproject-setup-dependency-management\u002Fpackaging-python-clis-for-distribution\u002Fpublishing-a-python-cli-to-pypi","Publishing a Python CLI to PyPI",{"path":1094,"title":1095},"\u002Fproject-setup-dependency-management\u002Fpoetry-workflows-for-cli-development","Poetry Workflows for CLI Development",{"path":1097,"title":1098},"\u002Fproject-setup-dependency-management\u002Fpoetry-workflows-for-cli-development\u002Fpoetry-entry-points-and-scripts-for-clis","Poetry Entry Points and Scripts for CLIs",{"path":1100,"title":1101},"\u002Fproject-setup-dependency-management\u002Fpre-commit-hooks-for-cli-projects","Pre-commit Hooks for CLI Projects",{"path":1103,"title":1104},"\u002Fproject-setup-dependency-management\u002Fpre-commit-hooks-for-cli-projects\u002Fsetting-up-pre-commit-for-python-cli-repos","Setting up pre-commit for Python CLI repos",{"path":1106,"title":1107},"\u002Fproject-setup-dependency-management\u002Fuv-for-python-cli-dependency-management","uv for Python CLI Dependency Management",{"path":1109,"title":1110},"\u002Fproject-setup-dependency-management\u002Fuv-for-python-cli-dependency-management\u002Fuv-init-vs-poetry-init-for-cli-tools","uv init vs poetry init for CLI tools",{"path":1112,"title":1113},"\u002Fproject-setup-dependency-management\u002Fuv-for-python-cli-dependency-management\u002Fuv-tool-install-vs-pipx-for-clis","uv tool install vs pipx for CLIs",{"path":1115,"title":1116},"\u002Fproject-setup-dependency-management\u002Fvirtual-environments-isolation-best-practices","Python CLI Env Isolation Best Practices",{"path":1118,"title":1119},"\u002Fproject-setup-dependency-management\u002Fvirtual-environments-isolation-best-practices\u002Fmanaging-virtual-environments-for-cross-platform-clis","Managing Python CLI Virtual Environments",{"path":1121,"title":1122},"\u002Fproject-setup-dependency-management\u002Fvirtual-environments-isolation-best-practices\u002Fpinning-the-python-version-for-a-cli","Pinning the Python Version for a CLI",1785614690029]