pypa / setuptools

Official project repository for the Setuptools build system
https://pypi.org/project/setuptools/
MIT License
2.36k stars 1.15k forks source link

Restore support for newlines in Summary #2893

Closed kiorky closed 2 years ago

kiorky commented 2 years ago

setuptools version

>= 59

Python version

3.8

OS

linux

Additional environment information

No response

Description

builds failing with

 File "/code/venv/lib/python3.8/site-packages/setuptools/dist.py", line 151, in single_line
   raise ValueError('Newlines are not allowed')

Expected behavior

Warning or nothing, just escape silently \n for oneline metadata fields

Lot of already released eggs wont ever comply to this policy, but we need to install them at the version they are pinned in our builds for the sake of reproducibility.

How to Reproduce

pip install django-hijack==2.1.10

Output

  ERROR: Command errored out with exit status 1:
   command: venv/bugpip/bin/python -c 'import io, os, sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-10qe4uxx/django-hijack_c7b8d72ff0ad41998610fe76e5822048/setup.py'"'"'; __file__='"'"'/tmp/pip-install-10qe4uxx/django-hijack_c7b8d72ff0ad41998610fe76e5822048/setup.py'"'"';f = getattr(tokenize, '"'"'open'"'"', open)(__file__) if os.path.exists(__file__) else io.StringIO('"'"'from setuptools import setup; setup()'"'"');code = f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /tmp/pip-pip-egg-info-awfzhldp
       cwd: /tmp/pip-install-10qe4uxx/django-hijack_c7b8d72ff0ad41998610fe76e5822048/
  Complete output (26 lines):
  running egg_info
  creating /tmp/pip-pip-egg-info-awfzhldp/django_hijack.egg-info
  writing /tmp/pip-pip-egg-info-awfzhldp/django_hijack.egg-info/PKG-INFO
  Traceback (most recent call last):
    File "<string>", line 1, in <module>
    File "/tmp/pip-install-10qe4uxx/django-hijack_c7b8d72ff0ad41998610fe76e5822048/setup.py", line 44, in <module>
      setup(name="django-hijack",
    File "venv/bugpip/lib/python3.8/site-packages/setuptools/__init__.py", line 153, in setup
      return distutils.core.setup(**attrs)
    File "/usr/lib/python3.8/distutils/core.py", line 148, in setup
      dist.run_commands()
    File "/usr/lib/python3.8/distutils/dist.py", line 966, in run_commands
      self.run_command(cmd)
    File "/usr/lib/python3.8/distutils/dist.py", line 985, in run_command
      cmd_obj.run()
    File "venv/bugpip/lib/python3.8/site-packages/setuptools/command/egg_info.py", line 292, in run
      writer(self, ep.name, os.path.join(self.egg_info, ep.name))
    File "venv/bugpip/lib/python3.8/site-packages/setuptools/command/egg_info.py", line 656, in write_pkg_info
      metadata.write_pkg_info(cmd.egg_info)
    File "/usr/lib/python3.8/distutils/dist.py", line 1117, in write_pkg_info
      self.write_pkg_file(pkg_info)
    File "venv/bugpip/lib/python3.8/site-packages/setuptools/dist.py", line 167, in write_pkg_file
      write_field('Summary', single_line(self.get_description()))
    File "venv/bugpip/lib/python3.8/site-packages/setuptools/dist.py", line 151, in single_line
      raise ValueError('Newlines are not allowed')
  ValueError: Newlines are not allowed

Code of Conduct

kiorky commented 2 years ago

cf https://github.com/pypa/setuptools/issues/1390#issuecomment-971585235

mriedem commented 2 years ago

Came here to report the same. Last week setuptools-58.5.3 was OK but now with setuptools-59.1.1 we're failing to install this package because of the newline in the description:

https://github.com/qiskit-community/qiskit-textbook/blob/stable/qiskit-textbook-src/setup.py#L8

Was this a regression from #1390?

mriedem commented 2 years ago

Was this a regression from #1390?

Oh I guess this is intentional in v59.0.0:

https://github.com/pypa/setuptools/blob/main/CHANGES.rst#v5900

Per #2870.

jaraco commented 2 years ago

As mentioned in #1390, this change was introduced intentionally in order to prevent these invalid inputs.

It sounds like this issue only affects older packages installed with the latest Setuptools. These environments are pinning their requirements but not pinning Setuptools. As a result, the maintainers of these environments are asking Setuptools to carry the burden of the legacy introduced by pinning their requirements.

I'd prefer for these projects to advance their dependencies to versions that no longer have the defective usage. But maybe there are other workarounds as well.

Why can't these environments work around the issue by pinning Setuptools as well? Or find another workaround such as installing from wheels (built from older Setuptools)?

For example, I was able to build the affected package thus:

draft $ pip-run -q 'setuptools<59' wheel -- -m pip wheel --no-deps django-hijack==2.1
Collecting django-hijack==2.1
  Using cached django-hijack-2.1.0.tar.gz (16 kB)
  Preparing metadata (setup.py) ... done
Building wheels for collected packages: django-hijack
  Building wheel for django-hijack (setup.py) ... done
  Created wheel for django-hijack: filename=django_hijack-2.1.0-py3-none-any.whl size=24148 sha256=51049e0c5eadb29e31a8c393db68891b61376eeef8311920048a7d3fd97bb838
  Stored in directory: /Users/jaraco/Library/Caches/pip/wheels/8b/57/f0/2cecaf262d2f06211ab69602ac2cd5d64cb093068cb7ee1c65
Successfully built django-hijack
draft $ ls
django_hijack-2.1.0-py3-none-any.whl

I've tried to think of ways Setuptools could support these legacy cases but reject new cases, but I haven't been able to come up with a good approach. I'd really like to avoid leaving Setuptools indefinitely dependent on the legacy of older packages.

kiorky commented 2 years ago

It sounds like this issue only affects older packages installed with the latest Setuptools. These environments are pinning their requirements but not pinning Setuptools. As a result, the maintainers of these environments are asking Setuptools to carry the burden of the legacy introduced by pinning their requirements.

Because every project has it's own pace to release and needs stability and depinning/upgrading those requirements imply extra maintenance and tests time and in case of paid work: costs which are not justified here. Version pinning is essential to ensure your build is reproducible. Breaking a build just because a metadata in a dependence, which may not be direct misses a \n which was not even mandatory at the time of the release of that dependence is a total non-sense.

I agree that quality should be enforced on new uploaded releases but installation of older packages should and must be still possible wiithout any change.

Seriously, guys, stripping eternally \n is not a big deal, and if we want to ensure packages now strictly validate, we just should ensure offending dists could not be uploaded.

Another (bad solution i think as it will break integrity of uploaded dists and break custom hash verifications) and also do not work with alternative mirrors where artefacts wont be patched is to patch already uploaded artefacts to make them comply with new code.

kiorky commented 2 years ago

Not to also mention that newer releases of dependences are maybe not compatible with every stack and every stack may not be that easily upgradable, just for an \n.

Seriously, reconsider that non sense breaking change...

jaraco commented 2 years ago

I've created a plugin, setuptools_hacks.bypass_summary_newline that provides a workaround for the issue. Users encountering this issue can install this plugin alongside setuptools 59+ to bypass the error by stripping everything from the first newline. Demo:

~ $ pip-run -q setuptools wheel setuptools_hacks.bypass_summary_newline -- -m pip-run --no-deps --no-binary :all: django-hijack==2.1.10 -- -c pass
Collecting django-hijack==2.1.10
  Using cached django-hijack-2.1.10.tar.gz (20 kB)
  Preparing metadata (setup.py) ... done
Skipping wheel build for django-hijack, due to binaries being disabled for it.
Installing collected packages: django-hijack
    Running setup.py install for django-hijack ... done
Successfully installed django-hijack-2.1.10

This approach allows these select environments dependent on the broken packages to work around the breakage while also retaining the check for new packages. It also clearly pushes the legacy maintenance to the environment maintainers instead of Setuptools itself.

kiorky commented 2 years ago

Sorry but no, its not AT ALL an acceptable solution, please reopen.

I don't want to figure out to push a new plugin in every build pipeline i have.

What we need is build stability and reproducibility.

Futherthemore, no one want to put a "legacy maintenance" on setuptools folks, we just want to have already and running fine build systems not to break for a \n detail.

jaraco commented 2 years ago

I appreciate your enthusiasm and concern. I too have managed private pipelines that would be affected by upstream changes in Setuptools, and it was indeed a hassle when an incompatible change to Setuptools, intentional or unintentional, would break the builds. Some of our projects avoided this issue by pinning Setuptools and only bumping it periodically. And while I would discourage that approach, it does provide for more stability in exchange for toil to occasionally update.

If reproducible builds is something you seek, then it's probably recommended not to leave Setuptools unpinned. Each release of Setuptools could potentially tweak the build, potentially changing the discovery of files or metadata format or a host of other behaviors.

Although the goal is to maintain compatibility, sometimes backward-incompatible changes are necessary. Setuptools has recently made much more impactful backward-incompatible changes, such as dropping support for Python 2 and 2to3. These changes are necessary to move Setuptools incrementally toward a more usable, maintainable, and correct solution, built on PyPA standards. This change also falls into that category.

Setuptools serves millions of downloads per day, and best I can tell, the fallout of this change has affected only a couple of environments. In my opinion, it doesn't make sense for Setuptools to adopt the indefinite burden of adapting incorrect inputs for a few cases. If this issue was widespread, I'd definitely like to consider it.

But when the issue is with a few select environments, I would implore the maintainers of those environments to find a way to adapt. Setuptools has provided several workarounds:

If it can be shown that these workarounds are inadequate or impose a grievous burden or affect a significant portion of the users, we can reconsider this issue.

kiorky commented 2 years ago

As you see, many users are beginning to fall upon the change, @jaraco, you should reconsider reopening this bug.

Here pinning setuptools down the road wont help in longterm maintenance, as sooner or later, the pin will have to be upgraded and no solution wont be here except adding a plugin in a stack, just for a newline that could be escaped... It's just crazy.

Already uploaded dists should just be as easily installable as the first day they were uploaded, point, you just broken many -AT-THE-UPLOAD-TIME valid artefacts.

What's wrong with you folks ?

We can totally understand that things should now be enforced, but already --valid-artefacts-- should still be installable, specially when it comes only to a newline metadata enforcement... So let this only fail for newly uploaded artefacts and at most warn for other already uploaded artefacts.

kiorky commented 2 years ago

related bugs

jaraco commented 2 years ago

As you see, many users are beginning to fall upon the change, @jaraco, you should reconsider reopening this bug.

You can also see from this change, the package owners were unaware of the defects they introduced by copying their long description into the description including any newlines. Without having introduced the breaking change, they may have gone on happily ignoring the warnings indefinitely, continuing to publish packages with broken metadata.

It occurred to me there's a possible fifth workaround:

What's wrong with you folks ?

Please limit your comments to the facts of the matter and your opinions, but avoid personal attacks. These won't be tolerated. Please read the PSF code of conduct and consider yourself warned.

To be sure, us folks is basically me. Other maintainers have not yet weighed in.

So let this only fail for newly uploaded artefacts and at most warn for other already uploaded artefacts.

I'm unaware of a way to do this. The validation that's happening is happening at the time that the metadata is written. That's the problem with these source artifacts - they're effectively indistinguishable from a package that was built from repo source.

I suppose it's conceivable that Setuptools could somehow detect the presence of a PKG-INFO from a previous build and if it detects invalid metadata, bypass the failure. I guess another way could be to enumerate all of the package/version combinations that are affected (essentially an allow-list for already-published artifacts).

related bugs

Thanks for collecting these (and linking them to increase visibility). I've read through these issues. It looks like a lot of them are issues that were only surfaced as a result of raising an error and probably would have continued to be broken except for this release. In that sense, the change is working as intended (creating the impetus to fix the broken usage).

It would be a real shame to introduce all this disruption only to once again allow the broken behavior to pass.

Thusfar, the only proposal to address the issue has been to back out the change and retain the warning behavior forever. In my opinion, that's unacceptable. I'd like to see a proposal that ends in Setuptools being able to enforce the standard.

kiorky commented 2 years ago

Please limit your comments to the facts of the matter and your opinions, but avoid personal attacks. These won't be tolerated. Please read the PSF code of conduct and consider yourself warned.

I stop you, nothing personal here. I do not understand the matter of risking to break stability for a bad prio \n stripping. I also agree with you that bad metadata is a real problem. Problem is that it was "tolerated" and we should act of this fact. Related bugs are only a few surfacing. How many like for django-hijack which isn't on the reported list are not yet surfacing ? Maybe scanning pypi, (i did not not to spam as it implies checking every version of every package), may give us some numbers ?

Indeed, i we saw, already released versions are still totally broken for installation right now. So that's not true that the existence of a newer release that's easyinstallable addresses the issue i'm reporting. Neither the existence of a setuptools plugin that would let me bypass the issue by obtaining the previous setuptools behavior.

I don't want to upgrade a (NON DIRECT) dependency of stable builds, i want to be able to install the release that was pinned, and battle tested. Specially for builds that we know are all plain of bad habits but we can't upgrade them to latest release because they are incompatibles. If there was a CVE or any other real bug of the artifact why not, but that not the case and that's a pity to be blocked just because of a spurious \n in a description field. Specially when newer versions of the incriminated package may not be compatible with vendor stacks puting your build at risk by the way incurring non justified extra maintenance costs.

I reassure you, we all know and we all pin everything, i hope, in stable stacks including setuptools itself, i hope. But it's only closing eyes and that problem will it us badly sooner or later. That's why we mostly all have also dev pipelines with some laxism in certain dependencies toinstallable anticipate such changes.

So let this only fail for newly uploaded artefacts and at most warn for other already uploaded artefacts. Problem is mainly for distributed packages, so a warning for users on localhost who don't want to patch their defective eggs and impossibility to upload is a good compromise.

I knew better 10 years ago than now setuptools code, and specially twine, but the command could fail at upload time. And server side speaking, we can also add an extra check as a belt and braces approach.

On the other hand, if nothing is done to revert that change, for already uploaded artifacts that would be scanned as broken, patching them by stripping out the \n that make them not installable may be a solution. In all cases, they won't be installable. Most of us rely on automatic hash verifications, breakages should be minor that right now with such a repair. In my case, that would, i hope, address the issue. And nothing would have then to be done to revert the new metadata enforcement.Technically speaking, i see this as way more tedious than stripping out \n.

nascheme commented 2 years ago

You can also see from this change, the package owners were unaware of the defects they introduced by copying their long description into the description including any newlines. Without having introduced the breaking change, they may have gone on happily ignoring the warnings indefinitely, continuing to publish packages with broken metadata.

Where were these warnings supposed to show up? I didn't see them and this afternoon when trying to build new container images, the build process failed, because of newlines in description for a (non-public) package the build depends on. Adding a plug-in or pinning setuptools doesn't seem like a great solution. I try to be diligent about fixing warnings that "pip" gives me so this breakage was unexpected. It turned what should be a 5 minute automated build into a long debugging process.

I have Python package dependencies pinned using "pip-compile" but I don't have "pip" and "setuptools" pinned since they get installed by "ensurepip" as part of the CPython build. I guess I need to go into that build automation and pin the pip and setuptools versions somehow. It seems there is a significant risk of new versions of setuptools breaking my builds.

I think pinning versions is a good idea in terms of security (e.g. avoiding malware injected into the supply chain). I think it's not so great when we have to do it for compatibility reasons. I recently had to spend quite a bit of time to try to get scipy to build for Python 3.10. They were pinning the build on Python <= 3.9. I hope this idea of pinning for compatibility doesn't spread more. Abseil's idea to "Live at Head"[1] is better.

  1. https://www.youtube.com/watch?v=tISy7EJQPzI&t=1032s
jaraco commented 2 years ago

Where were these warnings supposed to show up?

They show up in the pip log if you run with -v.

``` ~ $ pip-run -q 'setuptools<59' wheel -- -m pip-run -v --no-binary :all: --no-deps django-hijack==2.1.10 -- -c pass Using pip 21.3.1 from /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pip (python 3.10) Collecting django-hijack==2.1.10 Using cached django-hijack-2.1.10.tar.gz (20 kB) Running command python setup.py egg_info running egg_info creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-pip-egg-info-ioph6cek/django_hijack.egg-info writing /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-pip-egg-info-ioph6cek/django_hijack.egg-info/PKG-INFO /var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-run-lwuyk2bs/setuptools/dist.py:151: UserWarning: newlines not allowed and will break in the future warnings.warn("newlines not allowed and will break in the future") writing dependency_links to /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-pip-egg-info-ioph6cek/django_hijack.egg-info/dependency_links.txt writing requirements to /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-pip-egg-info-ioph6cek/django_hijack.egg-info/requires.txt writing top-level names to /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-pip-egg-info-ioph6cek/django_hijack.egg-info/top_level.txt writing manifest file '/private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-pip-egg-info-ioph6cek/django_hijack.egg-info/SOURCES.txt' reading manifest file '/private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-pip-egg-info-ioph6cek/django_hijack.egg-info/SOURCES.txt' reading manifest template 'MANIFEST.in' warning: no previously-included files matching '*.orig' found anywhere in distribution warning: no previously-included files matching '*.log' found anywhere in distribution warning: no previously-included files matching '*.swp' found anywhere in distribution no previously-included directories found matching 'hijack/tests/coverage' no previously-included directories found matching 'hijack/.ropeproject' adding license file 'LICENSE' adding license file 'AUTHORS' writing manifest file '/private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-pip-egg-info-ioph6cek/django_hijack.egg-info/SOURCES.txt' Preparing metadata (setup.py) ... done Skipping wheel build for django-hijack, due to binaries being disabled for it. Installing collected packages: django-hijack Running command /Library/Frameworks/Python.framework/Versions/3.10/bin/python3.10 -u -c 'import io, os, sys, setuptools, tokenize; sys.argv[0] = '"'"'/private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-install-7laxh3jc/django-hijack_131101cee3434fdc98893d6c92521c5e/setup.py'"'"'; __file__='"'"'/private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-install-7laxh3jc/django-hijack_131101cee3434fdc98893d6c92521c5e/setup.py'"'"';f = getattr(tokenize, '"'"'open'"'"', open)(__file__) if os.path.exists(__file__) else io.StringIO('"'"'from setuptools import setup; setup()'"'"');code = f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-record-49juixv3/install-record.txt --single-version-externally-managed --home /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo --compile --install-headers /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/include/python/django-hijack running install /var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-run-lwuyk2bs/setuptools/command/install.py:34: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools. warnings.warn( running build running build_py creating build creating build/lib creating build/lib/hijack copying hijack/signals.py -> build/lib/hijack copying hijack/models.py -> build/lib/hijack copying hijack/checks.py -> build/lib/hijack copying hijack/__init__.py -> build/lib/hijack copying hijack/apps.py -> build/lib/hijack copying hijack/admin.py -> build/lib/hijack copying hijack/settings.py -> build/lib/hijack copying hijack/urls.py -> build/lib/hijack copying hijack/helpers.py -> build/lib/hijack copying hijack/middleware.py -> build/lib/hijack copying hijack/views.py -> build/lib/hijack copying hijack/decorators.py -> build/lib/hijack creating build/lib/hijack/templatetags copying hijack/templatetags/__init__.py -> build/lib/hijack/templatetags copying hijack/templatetags/hijack_tags.py -> build/lib/hijack/templatetags creating build/lib/hijack/tests copying hijack/tests/__init__.py -> build/lib/hijack/tests copying hijack/tests/test_hijack.py -> build/lib/hijack/tests copying hijack/tests/utils.py -> build/lib/hijack/tests copying hijack/tests/south_settings.py -> build/lib/hijack/tests copying hijack/tests/test_hijack_url_settings.py -> build/lib/hijack/tests copying hijack/tests/manage_settings.py -> build/lib/hijack/tests copying hijack/tests/test_checks.py -> build/lib/hijack/tests copying hijack/tests/urls.py -> build/lib/hijack/tests copying hijack/tests/test_settings.py -> build/lib/hijack/tests creating build/lib/hijack/tests/test_app copying hijack/tests/test_app/authorization_checks.py -> build/lib/hijack/tests/test_app copying hijack/tests/test_app/models.py -> build/lib/hijack/tests/test_app copying hijack/tests/test_app/__init__.py -> build/lib/hijack/tests/test_app copying hijack/tests/test_app/urls.py -> build/lib/hijack/tests/test_app copying hijack/tests/test_app/views.py -> build/lib/hijack/tests/test_app copying hijack/tests/test_app/decorators.py -> build/lib/hijack/tests/test_app running egg_info writing django_hijack.egg-info/PKG-INFO /var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-run-lwuyk2bs/setuptools/dist.py:151: UserWarning: newlines not allowed and will break in the future warnings.warn("newlines not allowed and will break in the future") writing dependency_links to django_hijack.egg-info/dependency_links.txt writing requirements to django_hijack.egg-info/requires.txt writing top-level names to django_hijack.egg-info/top_level.txt reading manifest file 'django_hijack.egg-info/SOURCES.txt' reading manifest template 'MANIFEST.in' warning: no previously-included files matching '*.orig' found anywhere in distribution warning: no previously-included files matching '*.log' found anywhere in distribution warning: no previously-included files matching '*.swp' found anywhere in distribution no previously-included directories found matching 'hijack/tests/coverage' no previously-included directories found matching 'hijack/.ropeproject' adding license file 'LICENSE' adding license file 'AUTHORS' writing manifest file 'django_hijack.egg-info/SOURCES.txt' creating build/lib/hijack/locale creating build/lib/hijack/locale/cs creating build/lib/hijack/locale/cs/LC_MESSAGES copying hijack/locale/cs/LC_MESSAGES/django.mo -> build/lib/hijack/locale/cs/LC_MESSAGES copying hijack/locale/cs/LC_MESSAGES/django.po -> build/lib/hijack/locale/cs/LC_MESSAGES creating build/lib/hijack/locale/da creating build/lib/hijack/locale/da/LC_MESSAGES copying hijack/locale/da/LC_MESSAGES/django.mo -> build/lib/hijack/locale/da/LC_MESSAGES copying hijack/locale/da/LC_MESSAGES/django.po -> build/lib/hijack/locale/da/LC_MESSAGES creating build/lib/hijack/locale/de creating build/lib/hijack/locale/de/LC_MESSAGES copying hijack/locale/de/LC_MESSAGES/django.mo -> build/lib/hijack/locale/de/LC_MESSAGES copying hijack/locale/de/LC_MESSAGES/django.po -> build/lib/hijack/locale/de/LC_MESSAGES creating build/lib/hijack/locale/en creating build/lib/hijack/locale/en/LC_MESSAGES copying hijack/locale/en/LC_MESSAGES/django.mo -> build/lib/hijack/locale/en/LC_MESSAGES copying hijack/locale/en/LC_MESSAGES/django.po -> build/lib/hijack/locale/en/LC_MESSAGES creating build/lib/hijack/locale/es creating build/lib/hijack/locale/es/LC_MESSAGES copying hijack/locale/es/LC_MESSAGES/django.mo -> build/lib/hijack/locale/es/LC_MESSAGES copying hijack/locale/es/LC_MESSAGES/django.po -> build/lib/hijack/locale/es/LC_MESSAGES creating build/lib/hijack/locale/fr creating build/lib/hijack/locale/fr/LC_MESSAGES copying hijack/locale/fr/LC_MESSAGES/django.mo -> build/lib/hijack/locale/fr/LC_MESSAGES copying hijack/locale/fr/LC_MESSAGES/django.po -> build/lib/hijack/locale/fr/LC_MESSAGES creating build/lib/hijack/locale/pt_BR creating build/lib/hijack/locale/pt_BR/LC_MESSAGES copying hijack/locale/pt_BR/LC_MESSAGES/django.mo -> build/lib/hijack/locale/pt_BR/LC_MESSAGES copying hijack/locale/pt_BR/LC_MESSAGES/django.po -> build/lib/hijack/locale/pt_BR/LC_MESSAGES creating build/lib/hijack/locale/ru creating build/lib/hijack/locale/ru/LC_MESSAGES copying hijack/locale/ru/LC_MESSAGES/django.mo -> build/lib/hijack/locale/ru/LC_MESSAGES copying hijack/locale/ru/LC_MESSAGES/django.po -> build/lib/hijack/locale/ru/LC_MESSAGES creating build/lib/hijack/locale/sk creating build/lib/hijack/locale/sk/LC_MESSAGES copying hijack/locale/sk/LC_MESSAGES/django.mo -> build/lib/hijack/locale/sk/LC_MESSAGES copying hijack/locale/sk/LC_MESSAGES/django.po -> build/lib/hijack/locale/sk/LC_MESSAGES creating build/lib/hijack/static creating build/lib/hijack/static/hijack copying hijack/static/hijack/hijack-styles.css -> build/lib/hijack/static/hijack creating build/lib/hijack/templates creating build/lib/hijack/templates/hijack copying hijack/templates/hijack/notifications.html -> build/lib/hijack/templates/hijack copying hijack/templates/hijack/notifications_bootstrap.html -> build/lib/hijack/templates/hijack creating build/lib/hijack/tests/test_app/templates copying hijack/tests/test_app/templates/404.html -> build/lib/hijack/tests/test_app/templates copying hijack/tests/test_app/templates/hello.html -> build/lib/hijack/tests/test_app/templates copying hijack/tests/test_app/templates/hello_filter.html -> build/lib/hijack/tests/test_app/templates running install_lib creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/templatetags copying build/lib/hijack/templatetags/__init__.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/templatetags copying build/lib/hijack/templatetags/hijack_tags.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/templatetags copying build/lib/hijack/signals.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/sk creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/sk/LC_MESSAGES copying build/lib/hijack/locale/sk/LC_MESSAGES/django.mo -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/sk/LC_MESSAGES copying build/lib/hijack/locale/sk/LC_MESSAGES/django.po -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/sk/LC_MESSAGES creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/da creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/da/LC_MESSAGES copying build/lib/hijack/locale/da/LC_MESSAGES/django.mo -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/da/LC_MESSAGES copying build/lib/hijack/locale/da/LC_MESSAGES/django.po -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/da/LC_MESSAGES creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/pt_BR creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/pt_BR/LC_MESSAGES copying build/lib/hijack/locale/pt_BR/LC_MESSAGES/django.mo -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/pt_BR/LC_MESSAGES copying build/lib/hijack/locale/pt_BR/LC_MESSAGES/django.po -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/pt_BR/LC_MESSAGES creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/cs creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/cs/LC_MESSAGES copying build/lib/hijack/locale/cs/LC_MESSAGES/django.mo -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/cs/LC_MESSAGES copying build/lib/hijack/locale/cs/LC_MESSAGES/django.po -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/cs/LC_MESSAGES creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/ru creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/ru/LC_MESSAGES copying build/lib/hijack/locale/ru/LC_MESSAGES/django.mo -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/ru/LC_MESSAGES copying build/lib/hijack/locale/ru/LC_MESSAGES/django.po -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/ru/LC_MESSAGES creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/de creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/de/LC_MESSAGES copying build/lib/hijack/locale/de/LC_MESSAGES/django.mo -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/de/LC_MESSAGES copying build/lib/hijack/locale/de/LC_MESSAGES/django.po -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/de/LC_MESSAGES creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/fr creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/fr/LC_MESSAGES copying build/lib/hijack/locale/fr/LC_MESSAGES/django.mo -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/fr/LC_MESSAGES copying build/lib/hijack/locale/fr/LC_MESSAGES/django.po -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/fr/LC_MESSAGES creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/es creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/es/LC_MESSAGES copying build/lib/hijack/locale/es/LC_MESSAGES/django.mo -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/es/LC_MESSAGES copying build/lib/hijack/locale/es/LC_MESSAGES/django.po -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/es/LC_MESSAGES creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/en creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/en/LC_MESSAGES copying build/lib/hijack/locale/en/LC_MESSAGES/django.mo -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/en/LC_MESSAGES copying build/lib/hijack/locale/en/LC_MESSAGES/django.po -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/locale/en/LC_MESSAGES copying build/lib/hijack/models.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack copying build/lib/hijack/checks.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/test_app copying build/lib/hijack/tests/test_app/authorization_checks.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/test_app copying build/lib/hijack/tests/test_app/models.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/test_app copying build/lib/hijack/tests/test_app/__init__.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/test_app creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/test_app/templates copying build/lib/hijack/tests/test_app/templates/404.html -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/test_app/templates copying build/lib/hijack/tests/test_app/templates/hello.html -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/test_app/templates copying build/lib/hijack/tests/test_app/templates/hello_filter.html -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/test_app/templates copying build/lib/hijack/tests/test_app/urls.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/test_app copying build/lib/hijack/tests/test_app/views.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/test_app copying build/lib/hijack/tests/test_app/decorators.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/test_app copying build/lib/hijack/tests/__init__.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests copying build/lib/hijack/tests/test_hijack.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests copying build/lib/hijack/tests/utils.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests copying build/lib/hijack/tests/south_settings.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests copying build/lib/hijack/tests/test_hijack_url_settings.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests copying build/lib/hijack/tests/manage_settings.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests copying build/lib/hijack/tests/test_checks.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests copying build/lib/hijack/tests/urls.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests copying build/lib/hijack/tests/test_settings.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests copying build/lib/hijack/__init__.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack copying build/lib/hijack/apps.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack copying build/lib/hijack/admin.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/static creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/static/hijack copying build/lib/hijack/static/hijack/hijack-styles.css -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/static/hijack copying build/lib/hijack/settings.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/templates creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/templates/hijack copying build/lib/hijack/templates/hijack/notifications.html -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/templates/hijack copying build/lib/hijack/templates/hijack/notifications_bootstrap.html -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/templates/hijack copying build/lib/hijack/urls.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack copying build/lib/hijack/helpers.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack copying build/lib/hijack/middleware.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack copying build/lib/hijack/views.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack copying build/lib/hijack/decorators.py -> /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/templatetags/__init__.py to __init__.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/templatetags/hijack_tags.py to hijack_tags.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/signals.py to signals.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/models.py to models.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/checks.py to checks.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/test_app/authorization_checks.py to authorization_checks.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/test_app/models.py to models.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/test_app/__init__.py to __init__.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/test_app/urls.py to urls.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/test_app/views.py to views.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/test_app/decorators.py to decorators.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/__init__.py to __init__.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/test_hijack.py to test_hijack.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/utils.py to utils.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/south_settings.py to south_settings.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/test_hijack_url_settings.py to test_hijack_url_settings.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/manage_settings.py to manage_settings.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/test_checks.py to test_checks.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/urls.py to urls.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/tests/test_settings.py to test_settings.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/__init__.py to __init__.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/apps.py to apps.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/admin.py to admin.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/settings.py to settings.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/urls.py to urls.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/helpers.py to helpers.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/middleware.py to middleware.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/views.py to views.cpython-310.pyc byte-compiling /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/hijack/decorators.py to decorators.cpython-310.pyc running install_egg_info Copying django_hijack.egg-info to /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-target-80vsa2wo/lib/python/django_hijack-2.1.10-py3.10.egg-info running install_scripts writing list of installed files to '/private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-record-49juixv3/install-record.txt' Running setup.py install for django-hijack ... done Successfully installed django-hijack-2.1.10 ```

I do acknowledge that it's a poor experience that warnings from a backend are completely invisible during normal operation. Probably that's something that can be addressed in the packaging ecosystem (/pypa/packaging-problems), but not something that Setuptools can do.

I do observe that one can run with PYTHONWARNINGS=error and that will trigger errors on warnings, but unfortunately, that won't help if there are other unrelated warnings that error. Selecting down to UserWarning does reveal the error:

~ $ $PYTHONWARNINGS='error::UserWarning' pip-run -q 'setuptools<59' wheel -- -m pip-run -v --no-binary :all: --no-deps django-hijack==2.1.10 -- -c pass
Using pip 21.3.1 from /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pip (python 3.10)
Collecting django-hijack==2.1.10
  Using cached django-hijack-2.1.10.tar.gz (20 kB)
  Running command python setup.py egg_info
  running egg_info
  creating /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-pip-egg-info-yeuujh57/django_hijack.egg-info
  writing /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-pip-egg-info-yeuujh57/django_hijack.egg-info/PKG-INFO
  Traceback (most recent call last):
    File "<string>", line 1, in <module>
    File "/private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-install-grrjuapq/django-hijack_5f8edcb097854aea8e95cf55a6b59322/setup.py", line 44, in <module>
      setup(name="django-hijack",
    File "/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-run-jdny9588/setuptools/__init__.py", line 159, in setup
      return distutils.core.setup(**attrs)
    File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/distutils/core.py", line 148, in setup
      dist.run_commands()
    File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/distutils/dist.py", line 966, in run_commands
      self.run_command(cmd)
    File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/distutils/dist.py", line 985, in run_command
      cmd_obj.run()
    File "/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-run-jdny9588/setuptools/command/egg_info.py", line 292, in run
      writer(self, ep.name, os.path.join(self.egg_info, ep.name))
    File "/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-run-jdny9588/setuptools/command/egg_info.py", line 655, in write_pkg_info
      metadata.write_pkg_info(cmd.egg_info)
    File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/distutils/dist.py", line 1117, in write_pkg_info
      self.write_pkg_file(pkg_info)
    File "/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-run-jdny9588/setuptools/dist.py", line 167, in write_pkg_file
      write_field('Summary', single_line(self.get_description()))
    File "/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-run-jdny9588/setuptools/dist.py", line 151, in single_line
      warnings.warn("newlines not allowed and will break in the future")
  UserWarning: newlines not allowed and will break in the future
  Preparing metadata (setup.py) ... error
WARNING: Discarding https://files.pythonhosted.org/packages/17/1d/0ca80a5f49c34c363d273612e05d4772a760f9ef5ca838528f1f59c11cd8/django-hijack-2.1.10.tar.gz#sha256=be484f0ca67a092d5bf9bf8a5307beb716dc2e86b56a69796479183fdeb9036c (from https://pypi.org/simple/django-hijack/). Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.
ERROR: Could not find a version that satisfies the requirement django-hijack==2.1.10 (from versions: 0.1, 0.1.1, 0.1.2, 0.1.3, 0.1.4, 1.0.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4, 1.0.5, 1.0.6, 1.0.7, 1.0.8, 1.0.9, 1.0.10, 2.0.0, 2.0.1, 2.0.2, 2.0.3, 2.0.6, 2.0.7, 2.0.8, 2.1.0, 2.1.1, 2.1.2, 2.1.3, 2.1.4, 2.1.5, 2.1.6, 2.1.7, 2.1.8, 2.1.9, 2.1.10, 2.2.0, 2.2.1, 2.3.0, 3.0rc1, 3.0rc2, 3.0rc3, 3.0rc4, 3.0.0, 3.0.1, 3.0.2, 3.0.3, 3.0.4, 3.1.0, 3.1.1, 3.1.2)
ERROR: No matching distribution found for django-hijack==2.1.10

But still "UserWarning" doesn't match precisely. Better would be if the filter could match "SetuptoolsDeprecationWarning", but alas, the filter doesn't seem to be able to honor warning subclasses.

jaraco commented 2 years ago

I don't want to upgrade a (NON DIRECT) dependency of stable builds, i want to be able to install the release that was pinned, and battle tested. Specially for builds that we know are all plain of bad habits but we can't upgrade them to latest release because they are incompatibles.

It occurred to me that in this scenario, there's another option - the maintainer of an affected package can provide a bugfix to the specifically-affected versions in the wild. In the reported scenario, django-hijack==2.1.10 is affected, but the latest release is not. Presumably the version was pinned for a specific compatibility issue. Regardless, it's the responsibility of the person or project that pinned the version to address the consequences of the pin. In this case, I'd recommend for that user to reach out to the django-hijack project and request a new version be released with the easy fix, something like 2.1.11 or 2.1.10.1.

jaraco commented 2 years ago

In pypa/pip#10669, I've recognized a limitation in the build/install process that would make it difficult if not impossible to adopt some of the above workarounds, especially in a PEP 517 isolated build environment. To that end, I'm proposing an extension of pip that would give installers more control over the builds. If such a feature were to be implemented, it would readily facilitate working around issues like these.

kiorky commented 2 years ago

Seriously, I would be curious to find one maintainer that will be willing to patch every dotpoint release for a single \n metadata tweak. Specially on a big package which would be affected by this change. I just pretend to think that no one will ever have the time to spend on this, specially while being frustrated to see that all of their older packages are broken.

I understand the point of setuptools maintainers when we are going on the theory of "Who is responsible for an \n replacement in metadatas", but in real life, replacing it eternally with a is just a nobrainer question when it comes to stability and retrocompatibility that will upset lot of us, python users.

Also, i totally disagree on one responsibility point: you can't put it of each maintainer to patch all of their releases that were still legit at upload time and so should still remain installable except security problems (moreover when we are speaking of a \n metadata detail)...

Also, we, as users, don't want to use any "workaround" for such simple cases, as already said nascheme. So any of those is unacceptable in long term and really increase the maintenance burden: pinning way down eternally setuptools, using a pip plugin, patching requirements to use a constrained setuptools version as proposed in pypa/pip#10669, specially when it will come to mix versions for differents packages that have different needs.

(Dont misread me, details have all their importance, but they still have to be balanced)

jaraco commented 2 years ago

So let this only fail for newly uploaded artefacts and at most warn for other already uploaded artefacts.

I thought about this idea, but I don't think it's possible given the current standards for metadata. If Setuptools were to continue to build and only warn about these improper inputs, it would necessarily not be able to silently repair the broken packages. If it repairs the packages, then the uploader will never know that it was invalid at the source. If it doesn't repair the packages, then it will create invalid metadata for those old broken packages and any new ones that are uploaded with an older uploader that allows it. Moreover, if Setuptools doesn't repair the metadata, the output format will be difficult to detect (the extra newlines are valid in the format, but cause subsequent metadata fields to appear in the "body" of the metadata). Any uploader would need to do some clever inference to determine when a field was improperly formatted. Worse, there are some scenarios (where Summary appears as the last field) where it's literally impossible to detect the end of the Summary and the beginning of the Description (in the body).

jaraco commented 2 years ago

Seriously, I would be curious to find one maintainer that will be willing to patch every dotpoint release for a single \n metadata tweak. Specially on a big package which would be affected by this change. I just pretend to think that no one will ever have the time to spend on this, specially while being frustrated to see that all of their older packages are broken.

You're absolutely right. I wouldn't suggest to patch every one. I'd only suggest to patch the one or two that are in widespread use. If users are arbitrarily pinning to specific versions, it's on them to manage that maintenance burden. But if a large suite of users are pinning to a specific version for a reason intrinsic to the project (e.g. django-hijack>=2.2 breaks some environments), then providing maintenance updates for releases immediately prior may be feasible. I would expect the impacted user (kiorky in this example) to make the case to the project that the value to making a hotfix on an old release of the package outweighs the cost of options available to the downstream consumer.

replacing it eternally with a is just a nobrainer question when it comes to stability and retrocompatibility that will upset lot of us, python users.

Perhaps, but doing so also creates a crutch, causing users to end up with junk in their metadata (summaries that are multi-page descriptions, summaries with trailing whitespace), all the while thinking their package is perfectly satisfactory.

I don't have a fierce disagreement with anything you've said and for the most part, I agree. I simply don't want to lose the benefit of the efforts expended and indefinitely re-open the opportunity for projects to do the wrong thing. If extending the compatibility horizon by 6 months or a year would help, I'd be open to that. Or if there was some way to detect a previously-built package, I'd be open to that for some time. But regardless, I'd like to see some path that allows Setuptools to advance and not carry this burden. Currently, the best way I have to do that is with the backward-incompatible releases.

I have a question - have you explored use of the compatibility shim (hack)? Can you explain the difficulty in employing that in your pipelines (have you tried it in one pipeline; how much work was it; how many pipelines would be affected)?

kiorky commented 2 years ago

We fixed our pipelines by pinning setuptools<59.

Even for a couple of dotpoint releases, i keep this challenge open, no one will be willing to accept that burden just for a metadata tweak even if it's for a good reason. Also, i think that as users and maintainers of a package, we would quickly find what's wrong with a bad formatted metadata, specially in case it's really disruptive as the first negative impact will be on that package marketing. Also, if it ain't fixed, it's not a big deal, we can live with it, which is really not the case with this actual installation error.

Server-side metadata enforcement (EG: on pypi, before accepting such a sdist) would be a great place for a non uploadable check if it's not easily done elsewhere and force users to take care of non-newline fields.

For already released artifacts, as i already said, at most patch them to make them comply with new rules and eventually install, or only let them install like on the first day by reverting the introduced non compatible change.

jaraco commented 2 years ago

We fixed our pipelines by pinning setuptools<59.

That means things are working as intended. I apologize for the disruption, but I'm glad to hear that the workaround was straightforward and low impact. I do acknowledge this forces your project(s) to accept the maintenance burden of upgrading to non-broken versions of your dependencies before unpinning Setuptools. That's exactly the kind of tradeoff that we're asking projects to make (if in small numbers).

I thank you sincerely for your sharing your concerns in a civil and respectful way despite the anger I'm sure this change evoked.

At this point, I believe there's little more that we can do on this matter. I welcome further proposals or suggestions.

kiorky commented 2 years ago

Sorry, but no, this is just a temporary fix to continue operation and this bug is still present.

Formulating workarounds are not addressing the issue of keeping stability and retrocompatiblity.

As i said, we use longterm maintenance scenarii with everything pinned including setuptools, and short term ones where we don't just to detect by advance further breakages like this one, bingo... Although, we are considering the latest one to be less stable than the former, we assume it to keep green the most possible.

Here we are, to currently use a temporary workaround, and we so are considering our pipelines unstable and partially broken as this day and the solutions you proposed. We dont either want to stay blocked with a previous setuptools version which may become unsupported, or patch every single pipeline we got to add another one workaround plugin. Something is really wrong in this flow.

nascheme commented 2 years ago

Perhaps, but [replacing newlines] also creates a crutch, causing users to end up with junk in their metadata (summaries that are multi-page descriptions, summaries with trailing whitespace), all the while thinking their package is perfectly satisfactory.

It seems to me that the current cure is much worse than the disease. Couldn't new versions of setuptools refuse to do uploads of packages with junk in the metadata? Also, it could warn loudly when building or installing packages with bad metadata (not requiring the -v flag to see the warnings). If the description is so broken that it can't be used, I would suggest that using a generic description or no description at all would be better than outright refusing to install/build the package. Is junk in the description really a serious enough problem to make the package unusable? The package installed with previous versions of setuptools and the resulting application passed all of our QC testing. In my case, the package was never uploaded to PyPI and I don't think anything ever looked at the description.

jaraco commented 2 years ago

Couldn't new versions of setuptools refuse to do uploads of packages with junk in the metadata?

Setuptools doesn't do uploads, but even if it did, I explained above why it's not possible to detect the invalid data reliably.

Also, it could warn loudly when building or installing packages with bad metadata (not requiring the -v flag to see the warnings).

Yes, that's probably an action worth investigation. Unfortunately, it would probably require extensions to the standards between build frontends and build backends (such that warnings in the backend could be surfaced in the frontend). As it is now, pip only surfaces errors in the backend and otherwise hides the output. If someone is interested, that would be an interesting and valuable challenge to tackle, but certainly more than a quick patch.

If the description is so broken that it can't be used, I would suggest that using a generic description or no description at all would be better than outright refusing to install/build the package. Is junk in the description really a serious enough problem to make the package unusable?

See #1390 why this was an issue. Yes, it would be trivial to implement a change that transforms invalid input to valid metadata. That's what Setuptools has done for the past 6 months. Unfortunately, as you can see, that change had little if any effect on the invalid packages (even new releases). I've pointed out above I don't believe there's any way to distinguish between prior invalid metadata and brand new invalid metadata. If someone can propose such a mechanism, I'd be interested in exploring a fix.

In my case, the package was never uploaded to PyPI and I don't think anything ever looked at the description.

I'm glad to hear this change has helped you identify the invalid and correct the invalid metadata.

At this point, I'm convinced the change as drafted has had the intended effect (even if more disruptive than originally intended) and there are no open proposals on how to move forward. I still welcome proposals on how to move forward, but any proposal should include a proof-of-concept implementation, as the nuances seem to be getting lost in the discussion.

kiorky commented 2 years ago

I know that you are not a big fan of words with strength, but i won't be alone to think it's like sabotage.

I don't know what is bothering me more between knowing that setuptools broke retro-compatibility totally on purpose for no good reason (at least not enough legitimate to not let install a package that installed fine in the past and has no security concern today) or that something is not done to repair something which has an obvious flaw.

There is some kind of double talk when your users are still have to use workarounds to install packages, at equivalent pinning, and have no longterm solutions, and you just say them that there are solutions and "everything works as expected, no issue".

The issues you are describing here look like HTTP Smuggling, but the countermeasures taken are like using an sledgehammer to kill a fly. There is a balance to have, and also retro-compatibility to take in account... It would be as if you are asking the whole earth to have strictly W3C compliant HTML. We all know that the offenders are badly behaving, but most of time, it just works enough. IOW, we can live with a now badly indexed package, but there is no reason to make it's installation more difficult than before. No one is asking here setuptools to be responsible for bad metadatas and make you responsible for this.

As filtering out bad formatted fields is possible, it was already done before, But, also, detection is also possible, as you are doing it right now by raising the aforementioned exception, There is a serious flaw when we are breaking retro-compatibility instead of keeping to let install packages that were known to be 100%legit, Albeit the cosmetic metadatas that run out fine after some automatic data filtering.

@nascheme had a good idea to restore non breaking mode with a long, big, verbose message, without needed extra arguments, and maybe i would suggest to send a mail to maintainers on upload to pypi to make the issue really visible to maintainers. That should really help them to fix, but only on their new releases. If package maintainer want's its package to be correctly rendered or indexed, he will certainly fix the package metadatas, but there is no reason here to break all his past work, even if package metadatas are not 100% perfect.

But please, restore the old behavior or arrange it, or fix already released packages with the final purpose to let them install without errors as before and don't put this burden on users or maintainers (that should for them had to be done before letting them upload).

Arbitrary changing the rules of the game really would really upset most of us and the majority would always keep silent, and maybe doesn't even identify the root of the problem.

EDIT: Well the time.sleep() is maybe not a good idea as it will continue to break already released artifacts... removing it

jakoblover commented 2 years ago

Thanks for breaking my toolchain :) Doubt some of the packages I use will update their descriptions, so I will freeze setuptools to 58 for the foreseeable future.

jaraco commented 2 years ago

Thanks Jakob for reporting. Can you share some of the packages that affect your environment? I’d you consider using the workaround plugin so you wouldn’t have to pin Setuptools? If not, why not?

boegel commented 2 years ago

I strongly support the request to let setuptools strip out the excess newlines, rather than making this a hard error and essentially breaking the installation of existing Python package releases that happen to have a newline in the description field (see https://github.com/easybuilders/easybuild-easyblocks/pull/2623 for our particular case, where is happened by accident due to fixing code style issues that were being reported by flake8, and we were totally unaware of it).

I fully understand the motivation for trying to get people to not using newlines in packages (cfr. #1390), but frankly I think this is the wrong approach...

Considering looking at this from the point of view of someone who is not very familiar with installing Python packages. All of a sudden, they're starting to see errors like this:

ValueError: Newlines are not allowed

First of all: the error is very confusing, since it doesn't mention at all where newlines are not allowed. In the code? In the package name? What's going on?! In #2870 it was acknowledged that the error message could be better, and it was mentioned that "I expect these errors to be rare.". I think this lengthy discussion counters that last argument...

The first reaction from someone hitting this error is most likely going to be "I knew packaging was messy in Python, but this is just... wow". What's worse is that the end user has no easy way around it. I appreciate the suggested workarounds mentioned in https://github.com/pypa/setuptools/issues/2893#issuecomment-973173978, but I think these are outside of what a person who doesn't have extensive experience in installing Python packages would consider doing. Sure, most of those suggestions are targeted at package maintainers, but what if the package maintainer is unaware, or not responsive?

Even a simple workaround like using setuptools<59 is probably too much for non-experienced people. They often use whatever setuptools/pip/python comes with their OS, and they wouldn't even know where to begin to figure out this problem, let alone fix it. I'm very sure that most people just use pip install example. If that fails, they're likely to just give up quite quickly... (I don't applaud this, but I do feel it's a common reaction)

I have seen my share of problems with getting software installed, and I must say it took me a while to get my head around what was happening when I first ran into this problem. I simply couldn't understand why setuptools wouldn't just actively strip out the problematic newline characters, print a big fat warning, and then just go ahead and install the package anyway (because why wouldn't it).

Please consider changing this aggressive behavior of breaking a package installation for what most would consider a very silly problem that is easy to bypass (by just stripping out the newlines, replacing them with a space), before the setuptools versions that make this a hard error are being picked up in various Linux distributions and start causing more trouble around the world. For some environments (like Gentoo Prefix), it is already too late, but they will likely also pick up a new setuptools version, so hopefully the breakage can be short-lived.

It's now clear that there are dozens of Python packages out there that have a range of versions that are essentially no longer installable with a recent version of setuptools, although there's no good reason for it (since the workaround in setuptools is trivial).

I'm happy to contribute to help with working out a better approach for dealing with excess newlines in package descriptions, without massively disrupting the user experience.

fjsj commented 2 years ago

I just want my old packages to keep building... IMHO it makes no sense to introduce a breaking change on old released packages due to package metadata.

Yes, I could pin the setuptools version and that's exactly what I'll do, but I'm a developer with 10 years of Python experience: I can do that, I know pinning everything is necessary for reproducible and future-proof builds. But this issue is about the net gain for the community. What's the harm caused by this change and what's the gain for the Python community? Picture a beginner dev trying to install a not-so-old library that always had those newlines and getting this error. This change is basically blocking the usage of several old Python packages for many developers. This is not nice at all.

--- Edit: Right here: https://github.com/macports/macports-ports/commit/8477e9d5c3e03bb85cb461246ea4e2779b9e99bb there's an example of how even well-intended devs can make packages with this issue. For readability, the person might add newlines on description in setup.cfg! The dev is mislead by how setup.cfg works.

lucasjinreal commented 2 years ago

I upgraded setuptools just now on my server, and come to this error, then lead me to this discussion. for the reason why I stupid upgrade setuptools is that it introduced a lot of errors internally on setuptools old version.

I don't know what's wrong with newlines in description, but I strongly recommand avoid it gracefully rather than broken all packages in this world, this setuptools is so foundamental that it's not good to easily and ignore users experiences just for this tiny can be avoided newlines mistakes.

To be honest, please stop raise any errors, using a warning instead....

martyanov commented 2 years ago

The bug really shows what a mess is the current Python packaging ecosystem nowadays. There are many reasonable complaints in the thread. What should we do if we have several dated, but quite ok packages like https://github.com/jucacrispim/asyncblink?

@jaraco your workaround package with such an awkward name as setuptools-hacks.bypass-summary-newline feels like a trolling, sorry!

I'm pinning setuptools to v58.5.3 for now. :(

jaraco commented 2 years ago

@jaraco your workaround package with such an awkward name as setuptools-hacks.bypass-summary-newline feels like a trolling, sorry!

Perhaps it feels like trolling, but I assure you, it's not. It is meant to have a descriptive but unfriendly name to make it obvious to the person implementing it that it's meant to be a workaround with a very specific purpose and as a wart reflect that it's not an optimal solution. I know setuptools < 59 is simple and elegant, but it hides the purpose behind why it's there.

Here it is in action:

~ $ pip-run setuptools setuptools-hacks.bypass-summary-newline -q -- -m pip-run --no-binary asyncblink --no-deps asyncblink -- -c pass
Collecting asyncblink
  Using cached asyncblink-0.3.2.tar.gz (3.2 kB)
  Preparing metadata (setup.py) ... done
Skipping wheel build for asyncblink, due to binaries being disabled for it.
Installing collected packages: asyncblink
    Running setup.py install for asyncblink ... done
Successfully installed asyncblink-0.3.2

What should we do if we have several dated, but quite ok packages like https://github.com/jucacrispim/asyncblink?

This question feels like trolling, especially when there's a thread above that provides no less than 6 potential workarounds.

Additionally, I've provided guidance above on what I'd like to see to change the situation - a proposal with a demonstration of how you would go about fixing the issue. The only concrete, actionable proposal given thusfar is to just revert and allow the broken behavior forever. Other proposals are inactionable because they make assumptions and don't take into consideration the considerations I've enumerated above.

Furthermore, I don't get the impression that anyone has even tried the plugin.

I get it, you're offended that in principle setuptools should support building older packages. I agree. But the issue hasn't been important enough that someone could step up and come up with a solution or even volunteer to own the debt that reverting the change would have. And no one has stepped up to help with developing a solution between pip and build backends to surface warnings.

It's a lot of work just to roll out this change, first issuing the warning months ago, then following up, cutting a backward-incompatible release, then responding to the fallout. Now you're asking that we back out that change in order to satisfy a small number of broken packages, undoing that work and re-accumulating the debt.

And to be sure, the number and impact is larger than I had anticipated, but I'm asking for help here. Please help us make the ecosystem better by sending the signal to these packages that their metadata is invalid. Please help be a part of the solution. Thanks.

jaraco commented 2 years ago

In v59.4.0, I've reverted the change, restoring support for bad metadata.

kiorky commented 2 years ago

In v59.4.0, I've reverted the change, restoring support for bad metadata.

Thx @jaraco !