Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ModuleNotFoundError: No module named 'setuptools._distutils' #2353

Closed
dHannasch opened this issue Aug 31, 2020 · 21 comments
Closed

ModuleNotFoundError: No module named 'setuptools._distutils' #2353

dHannasch opened this issue Aug 31, 2020 · 21 comments
Labels

Comments

@dHannasch
Copy link

dHannasch commented Aug 31, 2020

pip install . suddenly started failing for many packages. Since setuptools just got a new version and pip didn't, and setuptools appears in the error, I'm guessing it's related to setuptools 50. Apologies if this turns out to be wrong.

This can be seen in any number of repositories, such as https://github.com/dHannasch/tox-sitepackages-example.

This doesn't appear to be quite the same as #2352.
I don't fully understand all the implications of https://github.com/pypa/setuptools/issues/2350, but SETUPTOOLS_USE_DISTUTILS=stdlib has no effect on this (ditto SETUPTOOLS_USE_DISTUTILS=1), so I think this is a separate issue.

Travis log:

$ python -m pip install .

Processing /home/travis/build/dHannasch/tox-sitepackages-example

  Installing build dependencies ... \/done

  Getting requirements to build wheel ... done

ERROR: Exception:

Traceback (most recent call last):

  File "/home/travis/build/dHannasch/tox-sitepackages-example/py38/lib/python3.7/site-packages/pip/_internal/cli/base_command.py", line 216, in _main

    status = self.run(options, args)

  File "/home/travis/build/dHannasch/tox-sitepackages-example/py38/lib/python3.7/site-packages/pip/_internal/cli/req_command.py", line 182, in wrapper

    return func(self, options, args)

  File "/home/travis/build/dHannasch/tox-sitepackages-example/py38/lib/python3.7/site-packages/pip/_internal/commands/install.py", line 325, in run

    reqs, check_supported_wheels=not options.target_dir

  File "/home/travis/build/dHannasch/tox-sitepackages-example/py38/lib/python3.7/site-packages/pip/_internal/resolution/legacy/resolver.py", line 183, in resolve

    discovered_reqs.extend(self._resolve_one(requirement_set, req))

  File "/home/travis/build/dHannasch/tox-sitepackages-example/py38/lib/python3.7/site-packages/pip/_internal/resolution/legacy/resolver.py", line 388, in _resolve_one

    abstract_dist = self._get_abstract_dist_for(req_to_install)

  File "/home/travis/build/dHannasch/tox-sitepackages-example/py38/lib/python3.7/site-packages/pip/_internal/resolution/legacy/resolver.py", line 340, in _get_abstract_dist_for

    abstract_dist = self.preparer.prepare_linked_requirement(req)

  File "/home/travis/build/dHannasch/tox-sitepackages-example/py38/lib/python3.7/site-packages/pip/_internal/operations/prepare.py", line 483, in prepare_linked_requirement

    req, self.req_tracker, self.finder, self.build_isolation,

  File "/home/travis/build/dHannasch/tox-sitepackages-example/py38/lib/python3.7/site-packages/pip/_internal/operations/prepare.py", line 91, in _get_prepared_distribution

    abstract_dist.prepare_distribution_metadata(finder, build_isolation)

  File "/home/travis/build/dHannasch/tox-sitepackages-example/py38/lib/python3.7/site-packages/pip/_internal/distributions/sdist.py", line 38, in prepare_distribution_metadata

    self._setup_isolation(finder)

  File "/home/travis/build/dHannasch/tox-sitepackages-example/py38/lib/python3.7/site-packages/pip/_internal/distributions/sdist.py", line 96, in _setup_isolation

    reqs = backend.get_requires_for_build_wheel()

  File "/home/travis/build/dHannasch/tox-sitepackages-example/py38/lib/python3.7/site-packages/pip/_vendor/pep517/wrappers.py", line 161, in get_requires_for_build_wheel

    'config_settings': config_settings

  File "/home/travis/build/dHannasch/tox-sitepackages-example/py38/lib/python3.7/site-packages/pip/_vendor/pep517/wrappers.py", line 265, in _call_hook

    raise BackendUnavailable(data.get('traceback', ''))

pip._vendor.pep517.wrappers.BackendUnavailable: Traceback (most recent call last):

  File "/home/travis/build/dHannasch/tox-sitepackages-example/py38/lib/python3.7/site-packages/pip/_vendor/pep517/_in_process.py", line 86, in _build_backend

    obj = import_module(mod_path)

  File "/opt/python/3.7.1/lib/python3.7/importlib/__init__.py", line 127, in import_module

    return _bootstrap._gcd_import(name[level:], package, level)

  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import

  File "<frozen importlib._bootstrap>", line 983, in _find_and_load

  File "<frozen importlib._bootstrap>", line 953, in _find_and_load_unlocked

  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed

  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import

  File "<frozen importlib._bootstrap>", line 983, in _find_and_load

  File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked

  File "<frozen importlib._bootstrap>", line 677, in _load_unlocked

  File "<frozen importlib._bootstrap_external>", line 728, in exec_module

  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed

  File "/opt/python/3.7.1/lib/python3.7/site-packages/setuptools/__init__.py", line 5, in <module>

    import distutils.core

  File "/tmp/pip-build-env-co0toouh/overlay/lib/python3.7/site-packages/_distutils_hack/__init__.py", line 82, in create_module

    return importlib.import_module('._distutils', 'setuptools')

  File "/opt/python/3.7.1/lib/python3.7/importlib/__init__.py", line 127, in import_module

    return _bootstrap._gcd_import(name[level:], package, level)

ModuleNotFoundError: No module named 'setuptools._distutils'
@dHannasch
Copy link
Author

dHannasch commented Aug 31, 2020

This one doesn't happen for every package, I haven't quite narrowed it down but I think it's related to having a pyproject.toml or setup_requires, but that might only be because having a build environment is what causes setuptools 50 to be installed in the first place.

@vkrot-exos
Copy link

I had same issue. Removing pyproject.toml from the library which failed to install pass through it

@dHannasch
Copy link
Author

dHannasch commented Aug 31, 2020

That's true, and a useful workaround, but I think that's just because with no pyproject.toml, setuptools 50 doesn't get installed in the first place.

So anyone can avoid this problem for now by specifying setuptools<50 in their pyproject.toml, but the fact that that works probably isn't relevant for debugging setuptools, sadly. Oh well.

E.g.

[build-system]
requires = [
    "setuptools>=30.3.0,<50",
    "wheel",
    "pytest-runner",
    "setuptools_scm>=3.3.1",
]

leycec added a commit to beartype/beartype that referenced this issue Aug 31, 2020
This commit resolves recent catastrophic upstream breakage introduced by
setuptools 50.0, the newest stable release of everyone's least favourite
build tool. Unrelatedly, this commit also optimizes a significant common
edge case when registering types with the beartypistry singleton (i.e.,
builtins) and exercises all remaining edge cases in kinds of parameters
annotated with PEP-compliant type hints.
(Glorious hoarfrost inundates specular peculiarities!)
@leycec
Copy link

leycec commented Aug 31, 2020

So, setuptools 50.0 is fundamentally broken and breaks the entire Python ecosystem – both open-source and not. Well, isn't that special. Heads need rolling (especially those currently attached to the still-functioning torsos of managerial project leads). Until the community tastes sweet vengeance, the following is a slightly saner solution than @dHannasch's excellent starting point:

[build-system]
requires = [
    "setuptools!=50.0",
    "wheel",
]

That is to say, downstream projects should probably only blacklist the specific version of setuptools known to catastrophically fail under the fairly safe assumption that the next stable release will either hopefully revert or perhaps even correctly fix the breakage.

I can confirm the above circumvention behaves as expected in a project just maliciously blind-sided by this packaging horror show. Passing tox-based tests or it didn't happen, of course.

Die, setuptools 50.0! Die! 🩸

@shakthifuture
Copy link

How to resolve this issue? please give us at least a temporary solution.

@simmel
Copy link

simmel commented Sep 1, 2020

@shakthifuture Setting environment variable SETUPTOOLS_USE_DISTUTILS=stdlib is a workaround, e.g.:

$ export SETUPTOOLS_USE_DISTUTILS=stdlib
$ pip3 install […]

@shakthifuture
Copy link

@simmel thank you, it working.

@jaraco
Copy link
Member

jaraco commented Sep 1, 2020

I'm unable to replicate the reported issue. When I attempt to install the reported package, it fails to install on another dependency:

~ $ pip-run git+https://github.com/dHannasch/tox-sitepackages-example
Collecting git+https://github.com/dHannasch/tox-sitepackages-example
  Cloning https://github.com/dHannasch/tox-sitepackages-example to /private/var/folders/qs/5jptvz2x7_gblx4kc3qj005800n8zm/T/pip-req-build-zqwijlld
  Installing build dependencies ... done
  Getting requirements to build wheel ... done
    Preparing wheel metadata ... done
Collecting click
  Using cached click-7.1.2-py2.py3-none-any.whl (82 kB)
ERROR: Could not find a version that satisfies the requirement detectron2 (from tox-sitepackages-example==0.0.1.dev15) (from versions: none)
ERROR: No matching distribution found for detectron2 (from tox-sitepackages-example==0.0.1.dev15)
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.8/bin/pip-run", line 10, in <module>
    sys.exit(run())
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/pip_run/__init__.py", line 18, in run
    with deps.load(*deps.not_installed(pip_args)) as home:
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/contextlib.py", line 113, in __enter__
    return next(self.gen)
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/pip_run/deps.py", line 52, in load
    _installable(args) and subprocess.check_call(cmd)
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/subprocess.py", line 364, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '('/Library/Frameworks/Python.framework/Versions/3.8/bin/python3.8', '-m', 'pip', 'install', '-t', '/var/folders/qs/5jptvz2x7_gblx4kc3qj005800n8zm/T/pip-run-1ued9qw2', 'git+https://github.com/dHannasch/tox-sitepackages-example')' returned non-zero exit status 1.

Is there an example I could follow to replicate the failure?

@jaraco
Copy link
Member

jaraco commented Sep 1, 2020

I've confirmed that setuptools 50 isn't strictly implicated. The tempora project uses a similar pyproject.toml and building it from source on Setuptools 50 works fine:

draft $ pip-run -q setuptools==50 -- -m pip-run --no-binary :all: tempora
Collecting tempora
  Using cached tempora-4.0.0.tar.gz (47 kB)
  Installing build dependencies ... done
  Getting requirements to build wheel ... done
    Preparing wheel metadata ... done
Collecting jaraco.functools>=1.20
  Using cached jaraco.functools-3.0.1.tar.gz (16 kB)
  Installing build dependencies ... done
  Getting requirements to build wheel ... done
    Preparing wheel metadata ... done
Collecting pytz
  Using cached pytz-2020.1.tar.gz (311 kB)
Collecting more-itertools
  Using cached more-itertools-8.5.0.tar.gz (86 kB)
  Installing build dependencies ... done
  Getting requirements to build wheel ... done
    Preparing wheel metadata ... done
Skipping wheel build for pytz, due to binaries being disabled for it.
Building wheels for collected packages: tempora, jaraco.functools, more-itertools
  Building wheel for tempora (PEP 517) ... done
  Created wheel for tempora: filename=tempora-4.0.0-py3-none-any.whl size=14543 sha256=fd76c8109f80e78da6acd5120ffb4ef2205383ce00566f183fcca6108c1737be
  Stored in directory: /Users/jaraco/Library/Caches/pip/wheels/de/73/95/5e7770fb0490fd6999ae7c4d1479fff7c179a3a9ee4389dec9
  Building wheel for jaraco.functools (PEP 517) ... done
  Created wheel for jaraco.functools: filename=jaraco.functools-3.0.1-py3-none-any.whl size=6716 sha256=5823cc422ea45a8d33396d23de223b7a5420ad965935770c3f2daa6a1da22264
  Stored in directory: /Users/jaraco/Library/Caches/pip/wheels/1f/c1/05/44ca6985d8e7c479c8074846f609769a77ccd275193bddc3b2
  Building wheel for more-itertools (PEP 517) ... done
  Created wheel for more-itertools: filename=more_itertools-8.5.0-py3-none-any.whl size=40626 sha256=b02870ffc1ec4b5a462c576f1c0b79683c65d774ec8013264184f96d03531d19
  Stored in directory: /Users/jaraco/Library/Caches/pip/wheels/89/6c/f0/c53ee96fbb1ef463ac154481e99ce2146921078d30845a89b3
Successfully built tempora jaraco.functools more-itertools
Installing collected packages: more-itertools, jaraco.functools, pytz, tempora
    Running setup.py install for pytz ... done
Successfully installed jaraco.functools-3.0.1 more-itertools-8.5.0 pytz-2020.1 tempora-4.0.0
>>> Python 3.8.1 (v3.8.1:1b293b6006, Dec 18 2019, 14:08:53) 
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
^D

The error message ModuleNotFoundError: No module named 'setuptools._distutils' suggests to me that there's some condition under which an older version of setuptools (earlier than 48) is present, but somehow the distutils hack is also present.

Please let me know what steps might be needed to create the environment in which your builds fail. If possible, put them together in a Dockerfile (preferred) or script that reproduces the issue. Thanks.

@bmw
Copy link

bmw commented Sep 1, 2020

This Dockerfile is somewhat convoluted, but this is the simplest way I've found to reproduce this problem so far:

FROM ubuntu:20.04
WORKDIR /root
RUN apt-get update && \
    apt-get install python3-dev python3-setuptools python3-venv python3-wheel gcc \
                    libssl-dev libffi-dev -y && \
    python3 -m venv --system-site-packages venv
ENV PATH /root/venv/bin:$PATH
RUN pip install --no-binary=:all: cryptography

EDIT: The installation succeeds if you set SETUPTOOLS_USE_DISTUTILS=stdlib

@godlygeek
Copy link

godlygeek commented Sep 1, 2020

I've been able to consistently reproduce this with a virtualenv created with --system-site-packages where setuptools < 50 is installed in the system site packages directory. I filed pypa/virtualenv#1934 for it because the behavior differs between virtualenv and venv. Update: Scratch that, after seeing #2353 (comment) I tried reproducing it with venv instead of virtualenv again and succeeded, so it's not in any way specific to virtualenv as opposed to venv. In any event the reproduction test case from that issue should still be useful.

Clearly there is some case where an isolated build isn't being properly isolated, and it's managing to pick up a different setuptools than the one that was installed for the isolated build.

@godlygeek
Copy link

godlygeek commented Sep 2, 2020

OK - I'm in no way an expert on PEP-517, but I think the bug here is in pip, not setuptools - or maybe there's one bug in each.

If a pyproject.toml contains something like:

[build-system]
requires = ["setuptools"]

then pip installs the latest version of setuptools into a temp directory with a name like (for instance):
/tmp/pip-build-env-5v90m1w9/overlay/lib/python3.8/site-packages

After it does that, it eventually calls the get_requires_for_build_wheel hook, in a new subprocess. In that subprocess, if pip was run inside a virtualenv created with --system-site-packages, then sys.path is something like:

['/home/matt/some_venv/lib/python3.8/site-packages/pip/_vendor/pep517',
 '/tmp/pip-build-env-5v90m1w9/site',
 '/usr/lib/python38.zip',
 '/usr/lib/python3.8',
 '/usr/lib/python3.8/lib-dynload',
 '/usr/local/lib/python3.8/dist-packages',
 '/usr/lib/python3/dist-packages',
 '/tmp/pip-build-env-5v90m1w9/overlay/lib/python3.8/site-packages',
 '/tmp/pip-build-env-5v90m1w9/normal/lib/python3.8/site-packages']

The overlay directory is after the system site packages directory, which means that any build dependencies installed by pip because they were required by the pyproject.toml are later in the search path than the ones that were already installed in the site-packages directory.

I think there's one bug in each here: the _distutils_hack probably shouldn't do anything if there's a different setuptools ahead of it in sys.path - whichever setuptools is earliest in the module search path should be completely responsible for everything; a .pth file from later in the search path shouldn't be screwing it up. And, pip probably shouldn't be calling the hook in an environment where already-installed modules are taking precedence over modules specifically required by the pyproject.toml - the whole point of an isolated build is to isolate you from what's installed system-wide.

@godlygeek
Copy link

There is, in fact, an old pip issue for exactly this bug. pypa/pip#6264 (comment).

@madzohan
Copy link

I had same issue. Removing pyproject.toml from the library which failed to install pass through it

man you've just saved my life 😄

sfermigier added a commit to abilian/abilian-core that referenced this issue Feb 9, 2021
sfermigier added a commit to abilian/abilian-sbe that referenced this issue Feb 9, 2021
lfpratik pushed a commit to lfpratik/grimoirelab-perceval that referenced this issue Jun 21, 2021
This code aims at aligning the CI tests across the
different grimoirelab components.

Python 3.5 is removed as its support period has ended and
Python 3.6, 3.7 and 3.8 are now supported.

The version of setuptools and pip has been downgraded as a
hotfix to solve the failing CI tests. It must be related to
the issue pypa/setuptools#2353.

Signed-off-by: Venu Vardhan Reddy Tekula <venu@bitergia.com>
Signed-off-by: lfpratik <pratikkaranje03@gmail.com>
djgalloway pushed a commit to ceph/ceph that referenced this issue Dec 20, 2021
pypa/setuptools#2353

Signed-off-by: David Galloway <dgallowa@redhat.com>
epuertat pushed a commit to ceph/ceph that referenced this issue Dec 20, 2021
pypa/setuptools#2353

Signed-off-by: David Galloway <dgallowa@redhat.com>
nizamial09 pushed a commit to rhcs-dashboard/ceph that referenced this issue Dec 21, 2021
pypa/setuptools#2353

Signed-off-by: David Galloway <dgallowa@redhat.com>
(cherry picked from commit 4ab2df1)
pechersky added a commit to pechersky/sendoff that referenced this issue Dec 28, 2021
Somehow, adding tbump as a dependency made tox upset with setuptools.

I regenerated the poetry.lock, and added SETUPTOOLS_USE_DISTUTILS=stdlib to the testenv envvars
based on pypa/setuptools#2353
qtprojectorg pushed a commit to qtproject/pyside-pyside-setup that referenced this issue Apr 22, 2022
Apparently, version 60.X (up including 62) introduces a bug
manifesting in imports not found:

ImportError: cannot import name 'build_py' from 'setuptools._distutils.command'

pypa/setuptools#2353

Change-Id: I4c08d61ed95998221fa560915011f5ad2ef8f58b
Reviewed-by: Christian Tismer <tismer@stackless.com>
leycec added a commit to betsee/betse that referenced this issue May 25, 2022
This commit resolves recent catastrophic upstream breakage introduced by
setuptools 50.0 and pip 22.2.0, the newest stable release of everyone's
least favourite build tools. Sadly, doing so requires temporarily
disabling project-wide support for the tooling-agnostic "pyproject.toml"
file -- which setuptools and pip continually demonstrate that they are
unwilling to sanely support.

## Issues Resolved

* #2, detailed above.
* #3, detailed below.
* pypa/setuptools#2353 and pypa/pip#6264, resolving recent catastrophic
  upstream breakage introduced by setuptools 50.0 and pip 22.2.0, the
  newest stable release of everyone's least favourite build tools.
  Sadly, doing so requires temporarily disabling project-wide support
  for the tooling-agnostic `pyproject.toml` file -- which setuptools and
  pip continually demonstrate that they are unwilling to sanely support.

(Unctuous tempestuousness of pestilence!)
leycec added a commit to betsee/betse that referenced this issue Jun 4, 2022
This minor release resolves a growing cacophony of compatibility issues
that have accrued in the two-and-a-half years since BETSE's last prior
stable release (i.e., BETSE 1.1.1 (Nicer Nestor) in the pre-pandemic
twilight of November, 2019). BETSE authors gratefully thank Dr. Levin at
the Levin Lab, Tufts University for his continued patronage of BETSE.

Noteworthy changes include:

## Hosting Improved

* **GitLab -> GitHub.** BETSE's `git` repository has been officially
  migrated from our prior host at GitLab to our new host at GitHub,
  mostly so as to centralize the maintenance burden of continuous
  integration (CI) workflows across this and the upstream @beartype
  project. Specifically:
  * Our prior GitLab-specific GitLab-CI and Windows-specific Appveyor CI
    configurations have been supplanted wholesale with our standard
    GitHub-specific GitHub Actions CI workflows for both package testing
    and publication.
  * Our front-facing `README.rst` documentation has been trivially
    revised to reference GitHub rather than GitLab.
  * Our prior GitLab-specific `tox.ini` configuration and
    `doc/rst/RELEASE.rst documentation have been supplanted wholesale
    with actively maintained and more GitHub-friendly equivalents
    liberally harvested from our upstream @beartype project.

## Compatibility Improved

* **Python >= 3.9.0.** BETSE now officially supports both the Python
  3.9.x and 3.10.x series.
* **macOS Aqua detection.** BETSE now detects the macOS-specific Aqua
  display server with significantly more robust logic. Previously, that
  detection unexpectedly raised exceptions under continuous integration
  (CI) workflows hosted by GitHub Actions. Now, that detection has been
  generalized to be resilient against edge cases – including:
  * The absence of the core macOS
    `/System/Library/Frameworks/Security.framework/Security` library.
  * The absence of the `OSError`-specific `strerror` instance variable
    on low-level exceptions raised during this detection.
* **Windows environment variable detection.** BETSE now circumvents
  erroneous Windows-specific shell environments produced by the GitHub
  Actions Windows runner, which fails to define the critical
  `%LOCALAPPDATA%`, `%APPDATA%`, or `%HOME` shell environment variables.
  Specifically:
  * The `betse.util.app.meta.AppMetaABC.dot_dirname()` method now
    leverages the first of the following shell environment variables
    exported to the active Windows process via trivial iteration:
    `%LOCALAPPDATA%`, `%APPDATA%`, and our standard `get_home_dirname()`
    getter. Insanely, the GitHub Actions Windows runner fails to export
    the former two shell environment variables as well as the otherwise
    standard `%HOME%` shell environment variable, which doesn't leave
    BETSE with terribly many options in CI environments.
* **Windows subdirectory detection.**  BETSE now circumvents the
  erroneous Windows-specific implementation of the standard
  `os.path.commonpath()` function, which unsafely raises an exception
  rather than safely returning `False` when passed two pathnames
  residing on different Windows drives (e.g., `C:/`, `D:/`). Our
  `betse.util.path.dirs.is_subdir()` tester now catches and coerces that
  unsafe exception into a safe return value of `False`.

## Compatibility Broken

* **Python < 3.8.0.** This release officially breaks backward
  compatibility by dropping support for the Python 3.5.x, 3.6.x, and
  3.7.x series. The former two have officially hit their End-of-Life
  (EOL); the latter is a half-year away from doing so and no longer
  worth officially supporting.
* **PIL (Pillow) < 7.0.0.** This release officially breaks backward
  compatibility by dropping support for PIL (Pillow) < 7.0.0. See below.

## Dependencies Bumped

* **NumPy ≥ 1.22.0.** BETSE now requires NumPy ≥ 1.22.0, which
  dramatically modified the (admittedly private) install-time
  `numpy.distutils.__config__` API describing how NumPy was linked
  against various external third-party shared libraries (e.g., BLAS,
  LAPACK) at install-time. Our `betse.lib.numpy.numpys` submodule now
  accesses that private NumPy API *much* more carefully – with robust
  future-proofing against predicted breakage by future NumPy releases
  breaking that API yet again.
* **PIL (Pillow) ≥ 7.0.0.** BETSE now requires PIL (Pillow) ≥ 7.0.0,
  which introduced the standard `pillow.__version__` attribute and
  deprecated the non-standard `pillow.PILLOW_VERSION` attribute. Doing
  so renders the codebase incompatible with PIL (Pillow) < 7.0.0.
  Relatedly, The
  `betse.util.py.module.pymodname.MODULE_NAME_TO_VERSION_ATTR_NAME`
  dictionary has removed the non-standard `PIL: 'PILLOW_VERSION'` entry.
* **`pytest` ≥ 5.4.0,** which refactored the previously defined private
  `_pytest.capture.CaptureManager._getcapture()` method into the newly
  defined `private _pytest.capture._getmulticapture()` function, which
  the `betse.lib.setuptools.command.supcmdtest` submodule necessarily
  monkey-patches at test time to sanitize captured output for
  long-running tests.

## Features Improved.

* **Runtime dependency validation.** BETSE now validates whether
  optional and mandatory dependencies satisfy requirements at
  application startup by manually validating dependency versions
  *before* deferring to increasingly unreliable `setuptools`-specific
  logic for doing so. Specifically:
  * The private `betse.lib.libs._iter_requirement_commands()` iterator
    has been generalized to accept items of the
    `REQUIREMENT_NAME_TO_COMMANDS` dictionary as codebase-agnostic
    tuples rather than codebase-specific
    `betse.metadata.RequirementCommand` instances.
  * In the `betse.lib.setuptools.setuptool` submodule:
    * The `die_unless_requirement(`) validator and `is_requirement()`
      tester have been generalized to manually validate dependency
      versions before deferring to `setuptools`-specific logic for doing
      so.
    * The `get_requirement_distribution_or_none()` getter docstring has
      been revised to note that that getter should *only* be called as a
      latch-ditch fallback.
* **Traceback handling.** BETSE now drastically simplifies its handling
  of exception tracebacks. Previously, BETSE *only* printed tracebacks
  (i.e., call stack traces) for uncaught exceptions when explicitly
  passed either the `-v` or `--verbose` options at the command line.
  Clearly, this was bad. While admittedly non-human-readable and thus
  unsightly, tracebacks yield mission-critical insights into critical
  breakage. Tracebacks are often the only means that devs have of
  debugging issues in cloud-hosted continuous integration (CI) workflows
  providing *no* convenient filesystem (and hence logfile) access;
  likewise, tracebacks are the only means that end users have of
  forwarding tracebacks to devs for subsequent debugging. BETSE now
  *always* prints tracebacks to standard error (stderr)
  regardless of options passed at the command line *or* defined in a
  configuration file.

## Issues Resolved.

* **Matplotlib stream plotting exception.** An exception previously
  raised by Matplotlib on animating streamplots has been resolved.
* **NumPy auto-object array deprecations.** All currently non-fatal
  `numpy.VisibleDeprecationWarning` warnings resulting from NumPy's
  recent deprecation of **auto-object arrays** (i.e., the implicit
  creation of one-dimensional NumPy arrays of Python `list` objects when
  passed ragged `list` of `list` objects with *no* uniform length) have
  been resolved. Where possible, these arrays have been reverted to
  standard non-NumPy `list` of `list` objects; in all other cases, these
  arrays have been explicitly coerced into non-auto object arrays.
* **Widespread deprecations.** A slew of other currently non-fatal
  deprecation warnings emitted by various third-party dependencies of
  BETSE have similarly been resolved.

## Installation Improved

* **Install-time Python version enforced.** The minimum mandatory
  version of Python required by this project is now enforced at
  `setuptools`-based install time via the recently introduced
  `python_requires` `setup(`) key in our top-level `setup.py` installer.
* **pypa/setuptools#2353 and pypa/pip#6264.** This release resolves
  recent catastrophic upstream breakage introduced by `setuptools` 50.0
  and `pip` 22.2.0, the newest stable release of everyone's least
  favourite build tools. Sadly, doing so requires temporarily disabling
  project-wide support for the tooling-agnostic `pyproject.toml` file --
  which `setuptools` and `pip` continually demonstrate that they are
  unwilling to sanely support.
* **`setuptools` entry point.** The `betse` command installed by our
  top-level `setup.py` installer now correctly runs. Previously, our
  installer inadvertently produced a broken `betse` command by
  incorrectly monkey-patching the `setuptools` installation process.

## Tests Improved

* **`pytest` warning resolved.** BETSE's test suite no longer issues a
  non-fatal warning under recent `pytest` versions. Specifically, our
  top-level `pytest.ini` configuration file now explicitly lists *all*
  custom `pytest` marks applied by our test suite.
* **`pytest` warnings filters.** BETSE now defers to `pytest` warnings
  filters when detected as running under `pytest` at test-time,
  preventing BETSE's custom warnings filters from silently overwriting
  those predefined by `pytest`. Most test harnesses (including `pytest`)
  define sane default warnings filters as well as enabling users to
  externally configure warnings filters from project-wide configuration
  files. Ergo, the `pytest` harness knows better than we do.
* **`tox` venv isolation validation.** BETSE's test suite now sports
  significantly improved `tox`-specific validation of whether this suite
  is safely isolated to a virtual environment (venv), avoiding spurious
  test failures with otherwise valid pipelines. Specifically:
  * The top-level `pytest`-specific `conftest.py` plugin has been
    generalized by:
    * Refactoring the existing private `_clean_imports()` function to:
      * Treat zipfiles on `sys.path` *not* isolated to the current `tox`
        venv as effectively isolated anyway, as zipfiles are effectively
        isolated from filesystem modification -- notably, `pip`- and
        `setuptools`-based package installation.
      * Reorder `sys.path` so as to shift import paths *not* isolated to
        the current `tox` venv to the end of this list, thus
        deprioritizing (but *not* removing) these paths.
    * Removed the obsolete private `_print_metadata()` function, most of
      which now resides in the `_clean_imports()` function.
* **PyPy testing disabled.** BETSE's test suite has (hopefully
  temporarily) disabled all testing of PyPy. Sadly, scientific
  dependencies (including NumPy) are *not* sanely installable under
  macOS + PyPy. macOS ships with Accelerate, blatantly broken
  macOS-specific BLAS and LAPACK shared libraries that fundamentally
  break NumPy on installation. Since NumPy fails to ship macOS + PyPy
  wheels, NumPy installation under macOS + PyPy requires building NumPy
  from source against Accelerate, which then painfully fails. For now,
  disabling PyPy testing entirely is the only sensible choice.
* **macOS test compatibility,** including:
  * The mostly incidental `test_dirs_get_mtime_newest()` unit test is
    now skipped under Apple macOS – due to what appear to be
    inconsistencies in the handling of nanosecond-resolution path
    timestamps under the macOS-specific HFS+ filesystem.

(*Infallible infancy bellows the fancy libel!*)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests