Repository: pypa/pip

Issue:
23.2: Unable to add zsh completion as documented
### Description

Attempting to follow the steps in the [documentation for adding zsh shell completion](https://pip.pypa.io/en/stable/user_guide/#command-completion) raises an error regarding the use of `compadd` outside of a completion function. This seems to be new with 23.2 and added in https://github.com/pypa/pip/pull/11417.

### Expected behavior

As in previous versions, following the documented steps should successfully add completion for pip.

### pip version

23.2

### Python version

3.11.4

### OS

macOS 12.6.8

### How to Reproduce

As documented:
1. Execute `python -m pip completion --zsh >> ~/.zprofile` or add eval "`pip completion --zsh`" to `.zprofile` or `.zshrc`
2. Start a new shell

### Output

From startup:
```
/dev/fd/16:compadd:4: can only be called from completion function
```

### Code of Conduct

- [X] I agree to follow the [PSF Code of Conduct](https://www.python.org/psf/conduct/).


Discussion:
@Freed-Wu as author of the mentioned pr what do you think of this issue?
Try:

```shell
$ python -m pip completion --zsh | tail -n+3 | tee /usr/share/zsh/site-functions/_pip
#compdef -P pip[0-9.]#
compadd $( COMP_WORDS="$words[*]" \
           COMP_CWORD=$((CURRENT-1)) \
           PIP_AUTO_COMPLETE=1 $words[1] 2>/dev/null )
# pip zsh completion end
```
A better solution is <https://github.com/pallets/click/pull/2544/files>
I will create a new PR to refer <https://github.com/pallets/click/pull/2544/files> to make `python -m pip completion --zsh > ~/.zprofile` work.

Resolving patch:
diff --git a/src/pip/_internal/commands/completion.py b/src/pip/_internal/commands/completion.py
--- a/src/pip/_internal/commands/completion.py
+++ b/src/pip/_internal/commands/completion.py
@@ -23,9 +23,18 @@
     """,
     "zsh": """
         #compdef -P pip[0-9.]#
-        compadd $( COMP_WORDS="$words[*]" \\
-                   COMP_CWORD=$((CURRENT-1)) \\
-                   PIP_AUTO_COMPLETE=1 $words[1] 2>/dev/null )
+        __pip() {{
+          compadd $( COMP_WORDS="$words[*]" \\
+                     COMP_CWORD=$((CURRENT-1)) \\
+                     PIP_AUTO_COMPLETE=1 $words[1] 2>/dev/null )
+        }}
+        if [[ $zsh_eval_context[-1] == loadautofunc ]]; then
+          # autoload from fpath, call function directly
+          __pip "$@"
+        else
+          # eval/source/. command, register function for later
+          compdef __pip -P 'pip[0-9.]#'
+        fi
     """,
     "fish": """
         function __fish_complete_pip


Test patch:
diff --git a/tests/functional/test_completion.py b/tests/functional/test_completion.py
--- a/tests/functional/test_completion.py
+++ b/tests/functional/test_completion.py
@@ -44,9 +44,18 @@
         "zsh",
         """\
 #compdef -P pip[0-9.]#
-compadd $( COMP_WORDS="$words[*]" \\
-           COMP_CWORD=$((CURRENT-1)) \\
-           PIP_AUTO_COMPLETE=1 $words[1] 2>/dev/null )""",
+__pip() {
+  compadd $( COMP_WORDS="$words[*]" \\
+             COMP_CWORD=$((CURRENT-1)) \\
+             PIP_AUTO_COMPLETE=1 $words[1] 2>/dev/null )
+}
+if [[ $zsh_eval_context[-1] == loadautofunc ]]; then
+  # autoload from fpath, call function directly
+  __pip "$@"
+else
+  # eval/source/. command, register function for later
+  compdef __pip -P 'pip[0-9.]#'
+fi""",
     ),
     (
         "powershell",