flake8-bugbear icon indicating copy to clipboard operation
flake8-bugbear copied to clipboard

16.4.0 cannot be built on Windows, causing pip dependency resolution failure

Open gschaffner opened this issue 3 years ago • 4 comments

Hi!

In a Windows VM, and in a bad case where pip's resolver backtracks all the way to version 16.4.0, pip will abort dependency resolution entirely. This is because the encoding of README.rst was not properly set before 379ef5b5fdc8a8a3a0e36fbc7f8cef5d12a900f8. On Windows the encoding will be assumed to be CP-1252 rather than UTF-8, so python setup.py egg_info will fail for 16.4.0 on Windows.

If 16.4.0 is yanked from PyPI (or yanked and reuploaded as 16.4.0.post1 with the encoding set properly), pip's resolver will not immediately halt when python setup.py egg_info fails and can thus try to backtrack with a different route that is more feasible than downgrading flake8-bugbear.

See also https://github.com/pypa/pip/issues/10655.

For example, in a Windows VM this issue can currently be triggered by:

$ pip install --dry-run 'flake8<=5.0.4' 'pyproject-flake8<=0.0.1a5' 'flake8-bugbear<=22.7.1' 'flake8-black<=0.3.3'
Collecting flake8<=5.0.4
  Using cached flake8-5.0.4-py2.py3-none-any.whl (61 kB)
Collecting pyproject-flake8<=0.0.1a5
  Using cached pyproject_flake8-0.0.1a5-py2.py3-none-any.whl (4.1 kB)
Collecting flake8-bugbear<=22.7.1
  Using cached flake8_bugbear-22.7.1-py3-none-any.whl (21 kB)
Collecting flake8-black<=0.3.3
  Using cached flake8_black-0.3.3-py3-none-any.whl (9.4 kB)
Collecting pyflakes<2.6.0,>=2.5.0
  Using cached pyflakes-2.5.0-py2.py3-none-any.whl (66 kB)
Collecting pycodestyle<2.10.0,>=2.9.0
  Using cached pycodestyle-2.9.1-py2.py3-none-any.whl (41 kB)
Collecting mccabe<0.8.0,>=0.7.0
  Using cached mccabe-0.7.0-py2.py3-none-any.whl (7.3 kB)
Collecting tomli
  Using cached tomli-2.0.1-py3-none-any.whl (12 kB)
Collecting flake8<=5.0.4
  Using cached flake8-4.0.1-py2.py3-none-any.whl (64 kB)
  Using cached flake8-4.0.0-py2.py3-none-any.whl (64 kB)
  Using cached flake8-3.9.2-py2.py3-none-any.whl (73 kB)
  Using cached flake8-3.9.1-py2.py3-none-any.whl (73 kB)
  Using cached flake8-3.9.0-py2.py3-none-any.whl (73 kB)
  Using cached flake8-3.8.4-py2.py3-none-any.whl (72 kB)
  Using cached flake8-3.8.3-py2.py3-none-any.whl (72 kB)
  Using cached flake8-3.8.2-py2.py3-none-any.whl (72 kB)
  Using cached flake8-3.8.1-py2.py3-none-any.whl (72 kB)
  Using cached flake8-3.8.0-py2.py3-none-any.whl (72 kB)
  Using cached flake8-3.7.9-py2.py3-none-any.whl (69 kB)
  Using cached flake8-3.7.8-py2.py3-none-any.whl (70 kB)
  Using cached flake8-3.7.7-py2.py3-none-any.whl (68 kB)
  Using cached flake8-3.7.6-py2.py3-none-any.whl (68 kB)
  Using cached flake8-3.7.5-py2.py3-none-any.whl (68 kB)
  Using cached flake8-3.7.4-py2.py3-none-any.whl (69 kB)
  Using cached flake8-3.7.3-py2.py3-none-any.whl (69 kB)
  Using cached flake8-3.7.2-py2.py3-none-any.whl (68 kB)
  Using cached flake8-3.7.1-py2.py3-none-any.whl (70 kB)
  Using cached flake8-3.7.0-py2.py3-none-any.whl (68 kB)
  Using cached flake8-3.6.0-py2.py3-none-any.whl (68 kB)
  Using cached flake8-3.5.0-py2.py3-none-any.whl (69 kB)
  Using cached flake8-3.4.1-py2.py3-none-any.whl (68 kB)
  Using cached flake8-3.4.0-py2.py3-none-any.whl (67 kB)
  Using cached flake8-3.3.0-py2.py3-none-any.whl (66 kB)
  Using cached flake8-3.2.1-py2.py3-none-any.whl (66 kB)
  Using cached flake8-3.2.0-py2.py3-none-any.whl (66 kB)
  Using cached flake8-3.1.1-py2.py3-none-any.whl (66 kB)
  Using cached flake8-3.1.0-py2.py3-none-any.whl (69 kB)
  Using cached flake8-3.0.4-py2.py3-none-any.whl (64 kB)
  Using cached flake8-3.0.3-py2.py3-none-any.whl (64 kB)
  Using cached flake8-3.0.2-py2.py3-none-any.whl (63 kB)
  Using cached flake8-3.0.1-py2.py3-none-any.whl (63 kB)
  Using cached flake8-3.0.0-py2.py3-none-any.whl (62 kB)
  Using cached flake8-2.6.2-py2.py3-none-any.whl (38 kB)
  Using cached flake8-2.6.1-py2.py3-none-any.whl (87 kB)
  Using cached flake8-2.6.0-py2.py3-none-any.whl (38 kB)
  Using cached flake8-2.5.5-py2.py3-none-any.whl (37 kB)
  Using cached flake8-2.5.4-py2.py3-none-any.whl (37 kB)
Collecting pep8!=1.6.0,!=1.6.1,!=1.6.2,>=1.5.7
  Using cached pep8-1.7.1-py2.py3-none-any.whl (41 kB)
Collecting flake8<=5.0.4
  Using cached flake8-2.5.3-py2.py3-none-any.whl (37 kB)
  Using cached flake8-2.5.2-py2.py3-none-any.whl (38 kB)
  Using cached flake8-2.5.1-py2.py3-none-any.whl (37 kB)
  Using cached flake8-2.5.0-py2.py3-none-any.whl (37 kB)
  Using cached flake8-2.4.1-py2.py3-none-any.whl (30 kB)
  Using cached flake8-2.4.0-py2.py3-none-any.whl (28 kB)
Collecting pep8<1.6,>=1.5.7
  Using cached pep8-1.5.7-py2.py3-none-any.whl (37 kB)
Collecting flake8<=5.0.4
  Using cached flake8-2.3.0-py2.py3-none-any.whl (23 kB)
Collecting attrs>=19.2.0
  Using cached attrs-22.1.0-py2.py3-none-any.whl (58 kB)
INFO: pip is looking at multiple versions of flake8-bugbear to determine which version is compatible with other requirements. This could take a while.
Collecting flake8-bugbear<=22.7.1
  Using cached flake8_bugbear-22.6.22-py3-none-any.whl (19 kB)
  Using cached flake8_bugbear-22.4.25-py3-none-any.whl (19 kB)
  Using cached flake8_bugbear-22.3.23-py3-none-any.whl (19 kB)
  Using cached flake8_bugbear-22.3.20-py3-none-any.whl (19 kB)
  Using cached flake8_bugbear-22.1.11-py3-none-any.whl (18 kB)
  Using cached flake8_bugbear-21.11.29-py36.py37.py38-none-any.whl (17 kB)
  Using cached flake8_bugbear-21.9.2-py36.py37.py38-none-any.whl (16 kB)
INFO: pip is looking at multiple versions of flake8-bugbear to determine which version is compatible with other requirements. This could take a while.
  Using cached flake8_bugbear-21.9.1-py36.py37.py38-none-any.whl (16 kB)
  Using cached flake8_bugbear-21.4.3-py36.py37.py38-none-any.whl (17 kB)
  Using cached flake8_bugbear-21.4.2-py36.py37.py38-none-any.whl (17 kB)
  Using cached flake8_bugbear-21.4.1-py36.py37.py38-none-any.whl (17 kB)
  Using cached flake8_bugbear-21.3.2-py36.py37.py38-none-any.whl (16 kB)
INFO: This is taking longer than usual. You might need to provide the dependency resolver with stricter constraints to reduce runtime. See https://pip.pypa.io/warnings/backtracking for guidance. If you want to abort this run, press Ctrl + C.
  Using cached flake8_bugbear-21.3.1-py36.py37.py38-none-any.whl (16 kB)
  Using cached flake8_bugbear-20.11.1-py36.py37.py38-none-any.whl (16 kB)
  Using cached flake8_bugbear-20.1.4-py36.py37.py38-none-any.whl (15 kB)
  Using cached flake8_bugbear-20.1.3-py36.py37.py38-none-any.whl (15 kB)
  Using cached flake8_bugbear-20.1.2-py36.py37.py38-none-any.whl (15 kB)
  Using cached flake8_bugbear-20.1.1-py36.py37.py38-none-any.whl (15 kB)
  Using cached flake8_bugbear-20.1.0-py36.py37.py38-none-any.whl (14 kB)
  Using cached flake8_bugbear-19.8.0-py35.py36.py37-none-any.whl (14 kB)
  Using cached flake8_bugbear-19.3.0-py35.py36.py37-none-any.whl (13 kB)
  Using cached flake8_bugbear-18.8.0-py35.py36.py37-none-any.whl (13 kB)
  Using cached flake8_bugbear-18.2.0-py35.py36-none-any.whl (18 kB)
  Using cached flake8_bugbear-17.12.0-py35.py36-none-any.whl (18 kB)
  Using cached flake8_bugbear-17.4.0-py35.py36-none-any.whl (17 kB)
  Using cached flake8_bugbear-17.3.0-py35.py36-none-any.whl (17 kB)
  Using cached flake8_bugbear-17.2.0-py35.py36-none-any.whl (16 kB)
  Using cached flake8_bugbear-16.12.2-py35.py36-none-any.whl (15 kB)
  Using cached flake8_bugbear-16.12.1-py35.py36-none-any.whl (14 kB)
  Using cached flake8_bugbear-16.12.0-py35.py36-none-any.whl (14 kB)
  Using cached flake8-bugbear-16.11.1.tar.gz (11 kB)
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'done'
  Using cached flake8-bugbear-16.11.0.tar.gz (9.7 kB)
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'done'
  Using cached flake8-bugbear-16.10.1.tar.gz (9.7 kB)
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'done'
  Using cached flake8-bugbear-16.10.0.tar.gz (9.6 kB)
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'done'
  Using cached flake8-bugbear-16.9.0.tar.gz (7.8 kB)
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'done'
  Using cached flake8-bugbear-16.7.1.tar.gz (7.5 kB)
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'done'
  Using cached flake8-bugbear-16.7.0.tar.gz (7.3 kB)
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'done'
Collecting black>=22.1.0
  Using cached black-22.6.0-cp310-cp310-win_amd64.whl (1.2 MB)
INFO: pip is looking at multiple versions of flake8-black to determine which version is compatible with other requirements. This could take a while.
Collecting flake8-black<=0.3.3
  Using cached flake8_black-0.3.2-py3-none-any.whl (9.3 kB)
  Using cached flake8_black-0.3.0-py3-none-any.whl (9.2 kB)
Collecting toml
  Using cached toml-0.10.2-py2.py3-none-any.whl (16 kB)
Collecting flake8-black<=0.3.3
  Using cached flake8_black-0.2.4-py3-none-any.whl (9.1 kB)
  Using cached flake8_black-0.2.3-py3-none-any.whl (8.9 kB)
  Using cached flake8_black-0.2.2-py3-none-any.whl (8.9 kB)
  Using cached flake8_black-0.2.1-py3-none-any.whl (8.9 kB)
  Using cached flake8-black-0.2.0.tar.gz (8.6 kB)
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'done'
INFO: pip is looking at multiple versions of flake8-black to determine which version is compatible with other requirements. This could take a while.
  Using cached flake8-black-0.1.2.tar.gz (8.5 kB)
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'done'
  Using cached flake8-black-0.1.1.tar.gz (8.3 kB)
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'done'
  Using cached flake8-black-0.1.0.tar.gz (6.9 kB)
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'done'
  Using cached flake8-black-0.0.4.tar.gz (6.0 kB)
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'done'
  Using cached flake8-black-0.0.3.tar.gz (5.9 kB)
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'done'
INFO: This is taking longer than usual. You might need to provide the dependency resolver with stricter constraints to reduce runtime. See https://pip.pypa.io/warnings/backtracking for guidance. If you want to abort this run, press Ctrl + C.
  Using cached flake8-black-0.0.2.tar.gz (5.8 kB)
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'done'
  Using cached flake8-black-0.0.1.tar.gz (5.6 kB)
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'done'
Collecting flake8-bugbear<=22.7.1
  Using cached flake8-bugbear-16.6.1.tar.gz (7.1 kB)
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'done'
  Using cached flake8-bugbear-16.6.0.tar.gz (6.9 kB)
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'done'
  Using cached flake8-bugbear-16.4.2.tar.gz (4.7 kB)
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'done'
  Using cached flake8-bugbear-16.4.0.tar.gz (3.3 kB)
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'error'
  error: subprocess-exited-with-error
  
  python setup.py egg_info did not run successfully.
  exit code: 1
  
  [8 lines of output]
  Traceback (most recent call last):
    File "<string>", line 2, in <module>
    File "<pip-setuptools-caller>", line 34, in <module>
    File "...\AppData\Local\Temp\pip-install-qma1uedw\flake8-bugbear_df34272b47e34ae7908d8f381c19fc83\setup.py", line 12, in <module>
      long_description = ld_file.read()
    File "...\lib\encodings\cp1252.py", line 23, in decode
      return codecs.charmap_decode(input,self.errors,decoding_table)[0]
  UnicodeDecodeError: 'charmap' codec can't decode byte 0x81 in position 1162: character maps to <undefined>
  [end of output]
  
  note: This error originates from a subprocess, and is likely not a problem with pip.
error: metadata-generation-failed

Encountered error while generating package metadata.

See above for output.

note: This is an issue with the package mentioned above, not pip.
hint: See above for details.

with pip 22.2.2. (I have tried to make the above command somewhat reproducible by pinning upper versions, but it might behave differently in the future e.g. if some packages publish new versions below those upper bounds enforced above.)

gschaffner avatar Aug 10 '22 04:08 gschaffner

If people on windows just use flake8-bugbear>=16.4.2 does this problem go away?

I'm more open to building a patched 16.4.0.post1, but feel fixing this here is the wrong place to do so and tooling should handle this ...

cooperlees avatar Aug 10 '22 17:08 cooperlees

Yeah, once the user figures out the cause of the problem, constraining flake8-bugbear>=16.4.2 does work around it. I wanted to post an issue here as it might save others time if they encounter the same issue.

Framing this issue in terms of pip resolution was probably misleading [1]. Maybe it is clearer to instead just point out that pip install flake8-bugbear==16.4.0 fails on Windows.

16.4.0 is the oldest version published on PyPI and was the most recent version for less than a month before 16.4.2. I doubt that very many people are using 16.4.0, and certainly nobody is using it on Windows unless they manually patched its sdist's setup.py or manually built a wheel on another OS. Instead of uploading a patched .post1 version it might be less work to just yank it, which would not impact any users who are still pinning to 16.4.0. Building a 16.4.0 wheel on non-Windows and uploading it would also work around the problem (albeit only for users not using pip install --no-binary ... flake8-bugbear).


[1]: I don't think that this is a problem with pip, except that pip's output could be clearer as to how constraints can be used to work around packaging issues of this type (see https://github.com/pypa/pip/issues/10655#issuecomment-991943149). There are some good reasons for pip behaving this way—see the linked issue. pip's behavior makes sense when either a maintainer uploaded built wheels to PyPI or when PyPI has no compatible wheel but building from sdist does not fail. Fails to build from an sdist are in my experience usually due to missing non-Python dependencies (e.g. GCC) on the user's system, so it usually makes sense to halt and present the issue to the user.

gschaffner avatar Aug 12 '22 02:08 gschaffner

Rightio - I guess yanking 16.4.0 since 16.4.2 has no code differences and just packaging fixes breaks effectively nothing and helps Windows users with this edge case. I appreciate your effort explaining and why you came here. It's all good stuff mate.

I'd like some other maintainers thoughts tho, @Zac-HD, @ambv, @carljm - See any issues with yanking our initial release we screwed up effectively?

cooperlees avatar Aug 12 '22 14:08 cooperlees

Yanking seems like the right response to me! Thanks for checking in @cooperlees - and thanks @gschaffner for your excellent report!

Zac-HD avatar Aug 12 '22 16:08 Zac-HD

Screen Shot 2022-08-16 at 8 29 52 PM

16.4.0 is yanked.

cooperlees avatar Aug 17 '22 03:08 cooperlees

Thank you!

gschaffner avatar Aug 17 '22 03:08 gschaffner