ZSH conflict with Python setuptools extras and pip?

I have installed python 3.9.7 using pyenv. I then installed a package webchanges which has optional dependencies to be installed using setuptools extras and pip format:

pip install --upgrade webchanges[use_browser]
pip install --upgrade webchanges[use_browser,redis]

however when using zsh I get this result:

$ python -m pip install --upgrade webchanges[use_browser]
zsh: no matches found: webchanges[use_browser]

when switching to bash the command completes without issue.

I am wondering what the message "no matches found" might indicate in relation to zsh? Is there a environmental setting that would confuse zsh when using the setuptools extras and pip syntax?

here is the full command history:

09/19/21_10:30:07 /Users/john
$ pip list
Package    Version
---------- -------
pip        21.2.4
setuptools 58.0.4

09/19/21_10:32:12 /Users/john
$ python -m pip install webchanges
Collecting webchanges
  Using cached webchanges-3.8.3-py3-none-any.whl (86 kB)
Collecting cssselect
  Using cached cssselect-1.1.0-py2.py3-none-any.whl (16 kB)
Collecting lxml
  Using cached lxml-4.6.3-cp39-cp39-macosx_10_9_x86_64.whl (4.6 MB)
Collecting html2text
  Using cached html2text-2020.1.16-py3-none-any.whl (32 kB)
Collecting markdown2
  Using cached markdown2-2.4.1-py2.py3-none-any.whl (34 kB)
Collecting pyyaml
  Using cached PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl (259 kB)
Collecting requests
  Using cached requests-2.26.0-py2.py3-none-any.whl (62 kB)
Collecting msgpack
  Using cached msgpack-1.0.2-cp39-cp39-macosx_10_14_x86_64.whl (74 kB)
Collecting platformdirs
  Using cached platformdirs-2.3.0-py3-none-any.whl (13 kB)
Collecting charset-normalizer~=2.0.0
  Using cached charset_normalizer-2.0.6-py3-none-any.whl (37 kB)
Collecting urllib3<1.27,>=1.21.1
  Using cached urllib3-1.26.6-py2.py3-none-any.whl (138 kB)
Collecting certifi>=2017.4.17
  Using cached certifi-2021.5.30-py2.py3-none-any.whl (145 kB)
Collecting idna<4,>=2.5
  Using cached idna-3.2-py3-none-any.whl (59 kB)
Installing collected packages: urllib3, idna, charset-normalizer, certifi, requests, pyyaml, platformdirs, msgpack, markdown2, lxml, html2text, cssselect, webchanges
Successfully installed certifi-2021.5.30 charset-normalizer-2.0.6 cssselect-1.1.0 html2text-2020.1.16 idna-3.2 lxml-4.6.3 markdown2-2.4.1 msgpack-1.0.2 platformdirs-2.3.0 pyyaml-5.4.1 requests-2.26.0 urllib3-1.26.6 webchanges-3.8.3

09/19/21_10:32:32 /Users/john
$ python -m pip install --upgrade webchanges[use_browser]
zsh: no matches found: webchanges[use_browser]


09/19/21_10:35:44 /Users/john
$ bash

The default interactive shell is now zsh.
To update your account to use zsh, please run `chsh -s /bin/zsh`.
For more details, please visit https://support.apple.com/kb/HT208050.
bash-3.2$ python -m pip install --upgrade webchanges[use_browser]
Requirement already satisfied: webchanges[use_browser] in ./.pyenv/versions/3.9.7/lib/python3.9/site-packages (3.8.3)
Requirement already satisfied: pyyaml in ./.pyenv/versions/3.9.7/lib/python3.9/site-packages (from webchanges[use_browser]) (5.4.1)
Requirement already satisfied: lxml in ./.pyenv/versions/3.9.7/lib/python3.9/site-packages (from webchanges[use_browser]) (4.6.3)
Requirement already satisfied: markdown2 in ./.pyenv/versions/3.9.7/lib/python3.9/site-packages (from webchanges[use_browser]) (2.4.1)
Requirement already satisfied: platformdirs in ./.pyenv/versions/3.9.7/lib/python3.9/site-packages (from webchanges[use_browser]) (2.3.0)
Requirement already satisfied: html2text in ./.pyenv/versions/3.9.7/lib/python3.9/site-packages (from webchanges[use_browser]) (2020.1.16)
Requirement already satisfied: requests in ./.pyenv/versions/3.9.7/lib/python3.9/site-packages (from webchanges[use_browser]) (2.26.0)
Requirement already satisfied: msgpack in ./.pyenv/versions/3.9.7/lib/python3.9/site-packages (from webchanges[use_browser]) (1.0.2)
Requirement already satisfied: cssselect in ./.pyenv/versions/3.9.7/lib/python3.9/site-packages (from webchanges[use_browser]) (1.1.0)
Collecting pyppeteer
  Using cached pyppeteer-0.2.6-py3-none-any.whl (83 kB)
Requirement already satisfied: urllib3<2.0.0,>=1.25.8 in ./.pyenv/versions/3.9.7/lib/python3.9/site-packages (from pyppeteer->webchanges[use_browser]) (1.26.6)
Collecting importlib-metadata>=1.4
  Using cached importlib_metadata-4.8.1-py3-none-any.whl (17 kB)
Collecting appdirs<2.0.0,>=1.4.3
  Using cached appdirs-1.4.4-py2.py3-none-any.whl (9.6 kB)
Collecting pyee<9.0.0,>=8.1.0
  Using cached pyee-8.2.2-py2.py3-none-any.whl (12 kB)
Collecting websockets<10.0,>=9.1
  Using cached websockets-9.1-cp39-cp39-macosx_10_9_x86_64.whl (88 kB)
Collecting tqdm<5.0.0,>=4.42.1
  Using cached tqdm-4.62.2-py2.py3-none-any.whl (76 kB)
Collecting zipp>=0.5
  Using cached zipp-3.5.0-py3-none-any.whl (5.7 kB)
Requirement already satisfied: charset-normalizer~=2.0.0 in ./.pyenv/versions/3.9.7/lib/python3.9/site-packages (from requests->webchanges[use_browser]) (2.0.6)
Requirement already satisfied: idna<4,>=2.5 in ./.pyenv/versions/3.9.7/lib/python3.9/site-packages (from requests->webchanges[use_browser]) (3.2)
Requirement already satisfied: certifi>=2017.4.17 in ./.pyenv/versions/3.9.7/lib/python3.9/site-packages (from requests->webchanges[use_browser]) (2021.5.30)
Installing collected packages: zipp, websockets, tqdm, pyee, importlib-metadata, appdirs, pyppeteer
Successfully installed appdirs-1.4.4 importlib-metadata-4.8.1 pyee-8.2.2 pyppeteer-0.2.6 tqdm-4.62.2 websockets-9.1 zipp-3.5.0
bash-3.2$ 

Solution 1:

TL,DR:

pip install --upgrade webchanges\[use_browser]

pip install --upgrade webchanges[use_browser] has mostly the same meaning in bash and zsh: webchanges[use_browser] is a wildcard pattern (“glob”). See bash pattern matching, zsh filename generation). […] means “one character, which is any of the characters between the brackets”. So if the current directory contains the files

webchanges webchangesa webchangesb webchangesc webchangesd webchangese webchangeser zebchangese

then the shell runs the command

pip install --upgrade webchangesb webchangese

Where bash and zsh differ is what happens when a pattern doesn't match any file. Bash (and other sh-compatible shells) pass the pattern unchanged to the command. Zsh, by default, signals an error.

The solution, in sh, bash or zsh, is to quote the special character [ (bash quoting, zsh quoting). You can quote ] for symmetry but you don't need to: it only has a special meaning when there's an unquoted [ before it to close. For example:

pip install --upgrade 'webchanges[use_browser]'
pip install --upgrade webchanges\[use_browser]
pip install --upgrade webchanges\[use_browser\]

Likewise you need to quote < or > in a pip requirement specifier so that it isn't parsed as indicating a redirection. In zsh you also need to quote either of the equal signs in == to avoid equal expansion if magic_equal_subst is enabled and equals is not disabled.

pip install webchanges\>1.2.3
pip install webchanges\>=1.2.3
pip install webchanges\==1.2.3
pip install webchanges=\=1.2.3