Skip to content

Tags: betsee/betse

Tags

v1.4.1

BETSE 1.4.1: The Resurrection of BETSE

Welcome to **BETSE 1.4.1,** the first stable release of BETSE in (*almost exactly*) two years! Let's model some bioelectricity. 🥳

Like all prior BETSE releases, **BETSE 1.4.1** is publicly available via both [`pip` + PyPI](https://pypi.org/project/betse) and [`conda` + @conda-forge](https://anaconda.org/conda-forge/betse). Pick your open-source packaging ~~poison~~ solution:

* **"I choose `pip`!"** *Excellent choice.* Everyone feels peppy with `pip`:

  ```bash
  pip install --upgrade betse
  ```

* **"I choose `conda`!"** *Also excellent.* You can't go wrong with reptiles:

  ```bash
  conda config --add channels conda-forge
  conda install betse
  ```

After upgrading to *any* new BETSE version (including **BETSE 1.4.1**), it's a great idea to validate that BETSE still behaves itself. Just run the ``betse try`` subcommand from a temporary directory you don't particularly care about: e.g.,

```bash
cd /tmp
betse try
```

You should see an animated plot of a sample bioelectric simulation subject to a **cut profile** (i.e., sudden removal of cells mimicking a tissue wound). If you see something *other* than that, see the "B-b-but Nothing Works..." section below. 😮‍💨

**Everything.** Literally everything. **BETSE 1.4.1** is a whole new computational beast.

Prior versions of BETSE (like BETSE 1.3.0) no longer run under a modern Python workflow. Incompatibilities with recent scientific dependencies (like NumPy 2.0.0, SciPy 1.14.0, and Matplotlib 3.9.0) render prior versions of BETSE all but unusable.

**BETSE 1.4.1** resolves *all* outstanding issues, deprecations, exceptions, and warnings by:

* Requiring a modern version of CPython: **CPython ≥ 3.11.** 🌝
* Requiring recent versions of most scientific dependencies, including:
  * **NumPy ≥ 2.0.0.** ✔️
  * **SciPy ≥ 1.14.0.** ✔️
  * **Matplotlib ≥ 3.9.0.** ✔️
  * **@beartype ≥ 0.18.0.** ✔️
* Refactoring the BETSE build system from the antiquated `setuptools` + `setup.py` <sup> 🤮 </sup> to [**Hatch**](https://github.com/pypa/hatch) + `pyproject.toml`. <sup> 🥂 </sup>
* Runtime type-checking the entire BETSE codebase with @beartype – a [hyper-fast standards-compliant type-checker designed from the ground-up for scientific workloads](https://github.com/beartype/beartype/). 😮

Having issues... *again*? Seeing a slew of exceptions? Drowning in a deluge of warnings? Fear not. We are here for you and your exciting research. Please submit:

* All [**issues** and **feature requests** directly to our friendly issue tracker here on **GitHub**](https://github.com/betsee/betse/issues). Let us assuage your valid problems.
* All [**scientific inquiries** and **collaborative inspiration** directly to Dr. Alexis Pietak at **ResearchGate**](https://www.researchgate.net/profile/Alexis-Pietak). Science!

The principal authors of BETSE – [Dr. Alexis Pietak (@pietakio)](https://www.researchgate.net/profile/Alexis-Pietak) and [Cecil Curry (@leycec)](https://github.com/leycec) – humbly thank *everyone* for your renewed interest in BETSE. Together, we will model all the bioelectricity.

We'd also like to congratulate [Dr. Andre Norfleet](https://bioengineering.gatech.edu/index.php/user/andre-norfleet) on his [recent explosive Cells 2024 submission: **"Identification of Distinct, Quantitative Pattern Classes from Emergent Tissue-Scale hiPSC Bioelectric Properties"**](https://www.mdpi.com/2073-4409/13/13/1136). This *super*-exciting paper leverages BETSE to validate experimental bioelectric findings. Due in large part to this paper's high-profile inclusion in the [Cells 2024 Special Issue **"Multiscale Studies of Cell Behavior"**](https://www.mdpi.com/journal/cells/special_issues/49AYA0UO06), we've fielded a sudden uptick of interest in using BETSE to validate similar findings *and* concern over the future well-being of BETSE.

Allow us to now say:

> **BETSE is here to stay.**

And BETSE 1.4.1 is the proof in the bioelectric pudding.

v1.4.0

BETSE 1.4.0: The Resurrection of BETSE

Welcome to **BETSE 1.4.0,** the first stable release of BETSE in (*almost exactly*) two years! Let's model some bioelectricity. 🥳

Like all prior BETSE releases, **BETSE 1.4.0** is publicly available via both [`pip` + PyPI](https://pypi.org/project/betse) and [`conda` + @conda-forge](https://anaconda.org/conda-forge/betse). Pick your open-source packaging ~~poison~~ solution:

* **"I choose `pip`!"** *Excellent choice.* Everyone feels peppy with `pip`:

  ```bash
  pip install --upgrade betse
  ```

* **"I choose `conda`!"** *Also excellent.* You can't go wrong with reptiles:

  ```bash
  conda config --add channels conda-forge
  conda install betse
  ```

After upgrading to *any* new BETSE version (including **BETSE 1.4.0**), it's a great idea to validate that BETSE still behaves itself. Just run the ``betse try`` subcommand from a temporary directory you don't particularly care about: e.g.,

```bash
cd /tmp
betse try
```

You should see an animated plot of a sample bioelectric simulation subject to a **cut profile** (i.e., sudden removal of cells mimicking a tissue wound). If you see something *other* than that, see the "B-b-but Nothing Works..." section below. 😮‍💨

**Everything.** Literally everything. **BETSE 1.4.0** is a whole new computational beast.

Prior versions of BETSE (like BETSE 1.3.0) no longer run under a modern Python workflow. Incompatibilities with recent scientific dependencies (like NumPy 2.0.0, SciPy 1.14.0, and Matplotlib 3.9.0) render prior versions of BETSE all but unusable.

**BETSE 1.4.0** resolves *all* outstanding issues, deprecations, exceptions, and warnings by:

* Requiring a modern version of CPython: **CPython ≥ 3.11.** 🌝
* Requiring recent versions of most scientific dependencies, including:
  * **NumPy ≥ 2.0.0.** ✔️
  * **SciPy ≥ 1.14.0.** ✔️
  * **Matplotlib ≥ 3.9.0.** ✔️
  * **@beartype ≥ 0.18.0.** ✔️
* Refactoring the BETSE build system from the antiquated `setuptools` + `setup.py` <sup> 🤮 </sup> to [**Hatch**](https://github.com/pypa/hatch) + `pyproject.toml`. <sup> 🥂 </sup>
* Runtime type-checking the entire BETSE codebase with @beartype – a [hyper-fast standards-compliant type-checker designed from the ground-up for scientific workloads](https://github.com/beartype/beartype/). 😮

Having issues... *again*? Seeing a slew of exceptions? Drowning in a deluge of warnings? Fear not. We are here for you and your exciting research. Please submit:

* All [**issues** and **feature requests** directly to our friendly issue tracker here on **GitHub**](https://github.com/betsee/betse/issues). Let us assuage your valid problems.
* All [**scientific inquiries** and **collaborative inspiration** directly to Dr. Alexis Pietak at **ResearchGate**](https://www.researchgate.net/profile/Alexis-Pietak). Science!

The principal authors of BETSE – [Dr. Alexis Pietak (@pietakio)](https://www.researchgate.net/profile/Alexis-Pietak) and [Cecil Curry (@leycec)](https://github.com/leycec) – humbly thank *everyone* for your renewed interest in BETSE. Together, we will model all the bioelectricity.

We'd also like to congratulate [Dr. Andre Norfleet](https://bioengineering.gatech.edu/index.php/user/andre-norfleet) on his [recent explosive Cells 2024 submission: **"Identification of Distinct, Quantitative Pattern Classes from Emergent Tissue-Scale hiPSC Bioelectric Properties"**](https://www.mdpi.com/2073-4409/13/13/1136). This *super*-exciting paper leverages BETSE to validate experimental bioelectric findings. Due in large part to this paper's high-profile inclusion in the [Cells 2024 Special Issue **"Multiscale Studies of Cell Behavior"**](https://www.mdpi.com/journal/cells/special_issues/49AYA0UO06), we've fielded a sudden uptick of interest in using BETSE to validate similar findings *and* concern over the future well-being of BETSE.

Allow us to now say:

> **BETSE is here to stay.**

And BETSE 1.4.0 is the proof in the bioelectric pudding.

v1.2.2

**BETSE 1.2.2** released.

This minor release brings titillating support for **configuring wrapper
logging** and **runtime type-checking wrapper parameters.**

This minor release resolves **3 issues.** Noteworthy changes include:

* **Wrapper logging configurability.** The high-level
  `betse.science.wrapper.BetseWrapper` class now enables logging to be
  configured at instantiation time. Specifically, the
  `BetseWrapper.__init__` method now accepts these optional
  logging-specific parameters:
  * `log_filename`, the relative or absolute filename of a file to
    append logging messages to. When running BETSE on a cluster, users
    are advised to set this parameter for each process-specific
    `BetseWrapper` instance to a filename isolated to that process.
    Doing so avoids non-deterministic failures when rotating log files,
    including a dreaded non-human-readable `"OSError: [Errno 116]
    Stale file handle"` exception emitted when attempting to log to a
    log file currently being rotated by a competing BETSE process.
  * `log_level`, a string-based enumeration configuring the global
    logging level (e.g., `"ALL"`, `"DEBUG"`, `"NONE"`). If specified,
    this global logging level overrides the optional `verbose` parameter
    accepted by various public methods of the `BetseWrapper` class.
* **Wrapper parameter runtime type-checking.** All parameters passed to
  all `BetseWrapper` methods are now type-checked at runtime via
  [`beartype`](https://github.com/beartype/beartype), a
  performance-friendly third-party pure-Python package.

* **Beartype ≥ 0.10.0.** BETSE now requires a reasonably recent version
  of [`beartype`](https://github.com/beartype/beartype) as a mandatory
  runtime dependency.

* **Fast solver `Dm_cells` export.** BETSE's fast solver now properly
  exports `Dm_cells` as a function of time.

* `license_file` -> `license_files` in `setup.cfg`. This release
  resolves a mostly negligible `setuptools` deprecation warning
  concerning the deprecated `license_file` setting in the top-level
  `setup.cfg` file:

     /usr/lib/python3.8/site-packages/setuptools/config/setupcfg.py:508:
     SetuptoolsDeprecationWarning: The license_file parameter is
     deprecated, use license_files instead.

* `numpy.distutil.__config__` -> `numpy.__config__`. This release
  resolves a deprecation warning emitted under NumPy ≥ 1.23.0 when
  importing *anything* from the `numpy.distutil` submodule.
  Specifically, this commit generalizes the `betse.lib.numpy.numpys`
  submodule to preferably import the actively maintained
  `numpy.__config__` submodule under NumPy ≥ 1.23.0 *before* falling
  back to importing the deprecated `numpy.distutil.__config__` submodule
  under NumPy < 1.23.0. Doing so improves the likelihood that BETSE will
  behave as expected under NumPy ≥ 1.24.0, where the deprecated
  `numpy.distutil` submodule is expected to be removed entirely.

* **Wrapper testing.** The high-level
  `betse.science.wrapper.BetseWrapper` class against a barrage of
  integration tests, including:
  * `test_wrapper_default()`, exercising that class with default
    parameters running an initialization phase.
  * `test_wrapper_logging()`, exercising that class with various
    non-default logging parameters running an initialization phase.
* **Test suite restructured.** Our test suite has been restructured so
  as to ensure that lower-level unit tests are exercised *before*
  higher-level integration tests -- improving the readability of test
  failures when the former fail.

(*Planiform plant form!*)

v1.2.1

**BETSE 1.2.1** released.

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:

* **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.

* **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`.

* **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.

* **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.

* **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.

* **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.

* **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.

* **`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!*)

v1.2.0

**BETSE 1.2.0** released.

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:

* **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.

* **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`.

* **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.

* **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.

* **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.

* **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.

* **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.

* **`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!*)

v1.1.1

BETSE 1.1.1 (Nicer Nestor) released.

Significant changes include:

* Dependencies bumped:
  * setuptools >= 38.2.0. The same version of setuptools required by
    BETSEE is now required by BETSE, ensuring parity between the two
    codebases and avoiding painful dependency conflicts. For uniformity,
    this dependency is repeated in both the top-level "pyproject.toml"
    and "tox.ini" configuration files.
* Issues resolved:
  * pypa/pip#6163 and pypa/pip#6434. This commit circumvents a countably
    infinite number of issues introduced by both pip 19.0.0 and 19.1.0 -
    all pertaining to PEP 517 and 518, which is to say "pyproject.toml".
    The mere existence of a "pyproject.toml" file fundamentally breaks
    pip in inexplicable and horrifying ways: a significantly worse state
    of affairs than the pre-PEP 517 and 518 days of setuptools yore. Say
    what you will of setuptools ("...so much hate"), but at least it
    sorta worked. The same cannot be said of recent pip versions, which
    absolutely do not work whatsoever. "Die, pip! Die!" Specifically:
    * pypa/pip#6163 resolved. All files and subdirectories of the
      project directory containing the top-level "setup.py" script are
      now safely registered to be importable modules and packages.
      Technically, this should not be needed. The current build
      framework (e.g., pip, setuptools) should implicitly guarantee this
      to be the case. Indeed, the obsolete setuptools-based
      "easy_install" script does so. Sadly, pip >= 19.0.0 fails to do so
      for projects defining a top-level "pyproject.toml" file. Although
      upstream purports to have resolved this, the most recent stable
      release of pip continues to suffer this. This is suffering.
    * pypa/pip#6434 resolved. The top-level "pyproject.toml" file now
      explicitly declares a default value for the "build-backend" key.
      Doing so safeguards backward compatibility with pip 19.1.0, which
      erroneously violated PEP 51{7,8} by failing to fallback to a sane
      default value in the absence of this key. If this key is left
      undeclared, pip 19.1.0 fails on attempting to perform an editable
      (i.e., developer-specific) installation of this project.
  * tox-dev/tox#765 resolved. "tox" bundles an obsolete version of
    "virtualenv" itself bundling obsolete versions of both "pip" and
    "setuptools", which obstructs sanity. Our "tox.ini" configuration
    resolves this issue by instructing "tox" to manually update both
    "setuptools" and "virtualenv" to their most recent stable releases.
  * Entry points (i.e., wrapper scripts) resolved. Specifically:
    * Windows-specific entry points installable. Entry points yet again
      install as expected under non-WSL Windows variants (i.e., vanilla,
      Cygwin Windows).
    * Dependency entry points preserved. Long-standing issues with a
      custom monkey patch applied by our top-level "setup.py" script at
      setuptools-based application installation time have now been
      resolved. Previously, this patch globally applied to all
      dependencies installed by "easy_install" (but neither "pip" nor
      "conda", which fortuitously remain unaffected). Now, this monkey
      patch only locally applies to this project and downstream
      consumers of this project (e.g., BETSEE).
  * Setuptools validation relaxed. Validation of the currently installed
    version of setuptools has been relaxed to accept recent non-standard
    version strings (e.g., "41.6.0.post20191029").
* Testing improved:
  * GitLab CI + "tox". Specifically:
    * Rewrote the top-level ".gitlab-ci.yml" file from the ground up to
      test both installation and runtime on all supported Python 3.x
      versions (rather than runtime on the most recent stable Python 3.x
      version) and leverage industry standards, including:
      * A test matrix exercising all official Python version-tagged
        Docker images rather than a single unofficial Python
        version-agnostic Docker image (i.e., "continuumio/anaconda3").
        Non-slim images are currently preferred to slim variants with
        names suffixed by "-slim" (e.g., "python:3.8-slim"), as the
        latter fail to include C and C++ compilers required to build and
        install wheels for Python packages both lacking official wheels
        and containing mandatory C extensions (e.g., "psutil").
      * The Python-specific "pip" and Debian-based "apt" package
        managers rather than the Anaconda-based "conda" package manager.
        An initial "apt" package cache is now explicitly created before
        attempting to install system-wide packages requiring this cache.
      * The high-level "tox" wrapper rather than the lower-level
        "pytest" test harness.
      * The standard "virtualenv" package rather than GitLab-CI
        internals for isolating test results between different Python
        3.x versions.
      * YAML anchor-based key mapping interpolation rather than external
        scripts for resolving DRY between different Python 3.x versions.
    * Defined a top-level "tox.ini" configuration file, installing all
      optional runtime dependencies to maximize test coverage.
    * Removed the top-level "requirements-pip.txt" file, which was
      frankly superfluous.
* Documentation revised:
  * Installation instructions rewritten. These instructions have been
    revised from the ground up for simplicity, consistency, and with an
    increased emphasis on the standard pip ecosystem rather than the
    increasingly non-standard Anaconda ecosystem. Specifically:
    * "README.rst" truncated. The top-level "README.rst" file now
      provides only minimal instructions for installing BETSE under the
      two most popular platform-agnostic package managers: pip
      (recommeded) and Anaconda.
    * "INSTALL.rst" added. A new "doc/rst/INSTALL.rst" file providing
      verbose platform-specific instructions for installing BETSE under
      each supported platform (i.e., Linux, macOS, Windows) as
      well as Git-based development and Docker-based containerization
      has now been added.
    * "INSTALL.md" obsoleted. The "doc/md/INSTALL.md" file has been
      removed in favor of the new "doc/rst/INSTALL.rst" file, whose
      underlying reStructuredText is significantly easier to maintain.
    * The sub-level "doc/rst/INSTALL.rst" file
  * pip-based editable installation. The top-level "README.rst" file now
    advises developers to leverage pip rather than setuptools when
    performing an editable installation of this project.
  * "setup.cfg"-based PyPI documentation. The top-level "setup.cfg" file
    now transcludes the contents of the top-level "README.rst" file, a
    substantial improvement over the prior code-based approach strewn
    throughout the codebase (e.g., "setup.py", "betse_setup.buputil").
  * Extraneous files removed. All irrelevant image masks have now been
    pruned from the "doc/yaml/paper" subdirectory. Doing so reduces the
    filesize of release tarballs, minimizes the maintenance burden, and
    presumably eliminates bitrot.
* Git maintenance:
  * pip directories untracked. All top-level pip-specific temporary
    directories (e.g., "pip-wheel-metadata") are now ignored with
    respect to Git tracking.
* API generalized:
  * Renamed:
    * The "betse.util.path.command" subpackage to
      "betse.util.os.command".
    * The "betse.util.test.tests" submodule to
      "betse.util.test.tsttest".
  * Generalized the top-level py.test-specific "conftest.py" plugin:
    * Defined a new private _clean_imports() function to perform the
      following when the active Python interpreter is isolated to a venv
      (e.g., due to being exercised by "tox"):
      1. If the top-level directory for this project is listed in the
         global list of all import directories (i.e., "sys.path"),
         remove this directory from this list. Doing so prevents this
         test session from accidentally importing from modules not
         isolated to this venv, including this project being tested.
      2. If the first directory on this list is not isolated to this
         venv, raise an exception. This condition implies that modules
         will be imported from outside this venv, which entirely defeats
         the purpose of isolating tests with "tox" to a venv.
      3. If the top-level "betse" package is not isolated to this venv,
         raise an exception. This condition implies that this project
         has been imported from outside this venv.
    * Refactored the pytest_sessionstart() hook run at test session
      startup to sanitize import directories by calling the newly
      defined _clean_imports() function. Specifically, if this session
      is isolated to a venv, this hook now removes all import
      directories not isolated to this venv from "sys.path" -- as
      required for sane "tox"-based testing. (See above.)
  * Generalized the top-level "setup.py" script:
    * Called the newly defined
      betse_setup.buputil.die_unless_setuptools_version_at_least()
      validator to ensure the currently installed version of setuptools
      satisfies all installation-time requirements.
    * Added a new "test" key-value pair to the setuptools-specific
      "extras_require" dictionary, enabling "tox" to install all
      mandatory testing requirements of this application without
      duplicating those requirements in the top-level "tox.ini" file.
  * Generalized the "betse.exceptions" submodule:
    * Defined new "BetseFunctionUnimplementedException",
      "BetseProcessException", and "BetseProcessNotFoundException"
      classes.
    * Renamed the "BetseOSShellEnvException" class to
      "BetseShellEnvException".
  * Generalized the "betse.metadeps" submodule:
    * Defined a new "SETUPTOOLS_VERSION_MIN" global, providing the
      minimum version of "setuptools" required at both install- and
      runtime. Previously, this global was inaccessibly hardcoded into
      the coarse-grained "RUNTIME_MANDATORY" global.
  * Generalized the "betse.lib.setuptools.command.supcommand" submodule:
    * Defined a new "SetuptoolsCommandDistributionTypes" global tuple of
      all distribution types commonly passed to methods called by the
      "setuptools.command.easy_install.easy_install" class, suitable for
      use in type checking.
  * Defined a new "betse.lib.setuptools.command.supcmdbuild" submodule,
    preserving the most critical of the contents of the prior
    implementation of the "betse_setup.bupbuild" submodule:
    * For safety, this submodule now safely monkey patches
      "easy_install" in an application-local manner.
    * For reusability, this submodule is now generically applicable to
      arbitrary applications -- including both BETSE and downstream
      consumers thereof (e.g., BETSEE).
    * Refactored the _scriptwriter_get_args_patched() class method to:
      * Accept any "ScriptWriter" or "VersionlessRequirement" instance
        as the passed "distribution" parameter.
      * Silently defer to the default ScriptWriter.get_args() method
        under non-WSL Windows variants (i.e., vanilla, Cygwin Windows).
  * Generalized the "betse.util.cli.cliabc" submodule:
    * Improved the CLIABC._options_top() method to print human-readable
      version specifiers when the "-V" and "--version" options are
      passed, complete with optional human-readable codename (e.g.,
      "BETSE 1.1.0 (Nice Nestor)").
  * Defined a new "betse.util.os.process" subpackage:
    * Defined a new "betse.util.os.process.prctest" submodule:
      * Defined a new is_process_command() tester, returning true only
        if the parent process of the active Python interpreter is
        running an external command with the passed basename. This
        function currently requires the optional "psutil" dependency --
        but could theoretically be expanded with platform-specific
        implementations circumventing this requirement.
  * Generalized the "betse.util.os.shell.shellenv" submodule:
    * Refactored the to_str() converter in terms of the
      mappings.to_str_flat() converter.
  * Defined a new "betse.util.py.module.pyimport" submodule:
    * Renamed the prior betse.util.py.pys.add_import_dirname() function
      to register_dir() for brevity and clarity.
    * Defined a new to_str_modules_imported_name() converter, returning
      a human-readable string of the fully-qualified names of all
      previously imported modules.
  * Defined a new "betse.util.py.pyvenv" submodule:
    * Defined a new is_venv() tester, returning true only if the active
      Python interpreter is isolated to a venv.
    * Defined a new get_system_prefix() getter, returning the absolute
      dirname of the top-level directory containing the system-wide
      Python interpreter.
  * Generalized the "betse.util.type.cls.classes" submodule:
    * Revised the die_unless_subclass() docstring to note that all
      classes are considered to be subclasses of themselves within the
      context of this function, complete with a minimal length example
      (MLE) demonstrating this.
  * Generalized the "betse.util.type.iterable.mapping.mappings"
    submodule:
    * Defined a new to_str_flat() converter, flattening the passed
      string-keyed and -valued dictionary into a human-readable string.
  * Refactored the "betse_setup.bupbuild" submodule to defer to the
    new "betse.lib.setuptools.command.supcmdbuild" submodule.
  * Generalized the "betse.lib.setuptools.command.supcmdtest" submodule:
    * Refactored the "test" subclass to inherit the test suite-specific
      "setuptools.command.test.test" superclass rather than the generic
      "setuptools.Command" superclass.
  * Generalized the "betse.util.path.dirs" submodule:
    * Renamed for disambiguity:
      * copy() to copy_dir().
      * copy_into_dir() to copy_dir_into_dir().
    * Defined a new die_if_subdir() function, raising an exception if
      the second passed directory is a subdirectory of the first passed
      directory.
    * Defined a new is_subdir() function, returning true if the second
      passed directory is a subdirectory of the first passed directory.
    * Defined a new remove_dir() function, recursively removing the
      passed directory after logging a warning and waiting several
      seconds for user intervention.
    * Revised docstrings for various existing functions.
  * Generalized the "betse.util.path.files" submodule:
    * Renamed for disambiguity:
      * remove() to remove_file().
      * remove_if_found() to remove_file_if_found().
  * Generalized the "betse.util.path.paths" submodule:
    * Renamed for disambiguity:
      * move() to move_path().
    * Defined a new remove_path() function, either recursively removing
      the passed path if a directory or non-recursively removing this
      path if a file.
  * Generalized the "betse.util.type.obj.objtest" submodule:
    * Defined a new die_unless_is() validator, raising an exception if
      any passed object is not identical to every passed object.
  * Refactored the "betse_setup.bupbuild" submodule to defer to the
    new "betse.lib.setuptools.command.supcmdbuild" submodule.
  * Generalized the "betse_setup.buputil" submodule:
    * Minimized this submodule to the smallest set of requisite
      functions required by the top-level "setup.py" script.
    * Defined a new die_unless_setuptools_version_at_least() validator,
      copied verbatim from the "betsee.beuputil" submodule.
    * Generalized the die_unless_setuptools_version_at_least() function
      to use the general-purpose "distutils.version.LooseVersion" class
      rather than the strict "distutils.version.StrictVersion" class.
  * Debugged the "betse_test.func.sim.test_sim" submodule:
    * Improved the test_cli_sim_compat() functional test to squelch
      (i.e., silence) warnings emitted by matplotlib during this test.
      Specifically, interactive visualizations are now disabled during
      this test by enabling the "betse_cli_sim_compat" fixture to
      overwrite the old simulation configuration required by this test.

(Bituminous arbiter delicately dangles the provocative invocation!)

v1.1.0

BETSE 1.1.0 (Nice Nestor) released.

Significant changes include:

* "--headless" option. The "betse" command now accepts a "--headless"
  option, permitting both end users and our test suite alike to
  explicitly enable headless operation. While the codebase did already
  detect most headless environments and thus automatically enable
  headless operation as needed, permitting interested parties to do so
  sanitizes numerous use cases.
* Backward compatibility guarantee extended to BETSE >= 0.5.0. BETSE now
  guarantees backward compatibility with simulation configurations
  supported by relatively recent versions of BETSE (i.e., BETSE 0.5.0
  and newer).
* PyYAML -> ruamel.yaml >= 0.15.24 required. BETSE now requires the
  comment- and whitespace-preserving "ruamel.yaml" YAML roundtripper
  rather than the antiquated PyYAML parser. Inadequacies in the Python
  installation ecosystem (e.g., setuptools, wheels) prevent BETSE from
  permissively accepting one of several different YAML implementations
  at installation time. Since "ruamel.yaml" is the superset of all YAML
  implementations, BETSE 1.1.0 officially drops PyYAML in favour of
  "ruamel.yaml".
* YAML 1.1 -> 1.2. All bundled YAML files have been trivially bumped
  from compliance with YAML 1.1 to 1.2 (i.e., the most recent YAML
  standard), resolving numerous valid complaints from "ruamel.yaml" at
  load time. To preserve backward compatibility with prior
  YAML-formatted simulation configuration files, files compliant with
  YAML 1.1 are implicitly interpreted as compliant with YAML 1.2
  instead.
* Mandatory dependencies bumped:
  * numpy >= 1.13.0. A recent version of numpy is now required, due to
    our widespread usage of the optional "axis" keyword argument passed
    to the numpy.unique() function first introduced by numpy 1.13.0.
    Doing so resolves a recent spate of CI failures in Windows-ish
    AppVeyor pipelines.
  * py.test >= 3.7.0. A recent version of py.test is now required, which
    introduced a new "package" scope for fixtures. This scope is
    required to efficiently initialize and deinitialize application
    metadata singletons for unit tests.
* Deprecation warnings resolved:
  * A deprecation from the "betse" script wrapper.
  * A deprecation from the numpy.unique() function on failing to
    explicitly pass the "axis" keyword argument.
* Numpy optimization detection. To improve detection of optimized Numpy
  installations (i.e., of a "numpy" package linked against a
  multithreaded BLAS implementation), application startup preferentially
  detects whether the active Python interpreter is managed by any of the
  following, in which case Numpy is guaranteed to be optimized:
  * The "conda" package manager.
  * The modern Gentoo Linux scientific stack (i.e., "eselect-ldso").
* Problematic matplotlib backends blacklisted (i.e., prohibited):
  * Unconditionally blacklisted all Qt 4-specific matplotlib backends
    (e.g., "Qt4Agg", "Qt4cairo"). The Qt Company ceased maintenance of
    Qt 4 in 2015, implying Qt 4 to be insecure, fragile, and largely
    unavailable.
  * Conditionally blacklisted all Qt 5-specific matplotlib backends
    (e.g., "Qt5Agg", "Qt5cairo") under headless environments. When
    headless, these backends reliably cause Qt 5 to silently fail with a
    segmentation fault.
* Logging improvements:
  * Logfile rotation race conditions resolved. A long-standing issue
    inviting race conditions between multiple processes contending for
    write access to rotational logfiles has now been resolved, thanks to
    intrepid end user Edward Ned Harvey at the Levin Lab. Thanks a
    metric ton, Ed!
  * Logfile closure. Open logfile handles are now guaranteeably closed
    in a platform-portable manner at application shutdown, resolving
    Windows-specific "ResourceWarning: unclosed file" warnings. Namely:
    * The application metadata singleton is now unconditionally
      deinitialized at application shutdown (even in the event of fatal
      exceptions).
    * Our default logging configuration now removes all existing
      handlers from the root logger and close all open file handles
      maintained by those handlers at both initialization and
      deinitialization time.
* Testing improvements:
  * "${DISPLAY}" squelched. The X.org-specific "${DISPLAY}" environment
    variable is now squelched during testing, resolving functional test
    failures.
  * Windows-specific warnings logged as informational non-warnings, a
    crude workaround to prevent PowerShell from erroneously raising
    non-human-readable exceptions on the first write to standard error.
    (Why!?)
  * "--export-sim-conf-dir" removed. The awkward "--export-sim-conf-dir"
    option previously accepted by our test suite has been obsoleted.
  * Our AppVeyor configuration now:
    * Locally resolves conda/conda#8836, a recent Windows-specific
      Anaconda change significantly breaking backward compatibility with
      most existing AppVeyor configurations -- including ours. To do so,
      the base Anaconda environment is now explicitly activated in an
      early-time manner.
    * Activates its "conda" environment via the "activate" command
      rather than the "conda activate" subcommand, which appears to be
      non-functional.
    * Coerces high-level PowerShell stream objects produced by running
      the "py.test" command into low-level strings. If omitted,
      PowerShell insanely interprets the first write to stderr as a
      non-human-readable exception.
  * Our GitLab-CI Runner configuration now:
    * Caches both "conda" environments and packages to project-local
      directories, which should reduce redundant pipeline overhead.
    * Imports environment variables required for sane "conda" usage and,
      in particular, the recently introduced "conda activate"
      subcommand.
    * Has been streamlined for porting to downstream consumers (e.g.,
      BETSEE):
      * Removed all disabled configuration pertaining to "pip".
        Leveraging "pip" from a Miniconda-based configuration is
        fundamentally wrong.
      * Printed application metadata before running the test suite via
        the new "--headless" option.
* Documentation improvements:
  * Docstring revisions for numerous subclasses in the
    "betse.science.config.export.visual" submodule for accuracy.
* API generalizations:
  * Renamed the prior:
    * "betse.metaapp" submodule to "betse.appmeta".
    * "betse.util.app.meta.metaappabc" submodule to
      "betse.util.app.meta.appmetaabc".
    * "betse.util.app.meta.appmetadep" submodule to
      "betse.util.app.meta.appmetamod".
    * "betse.util.app.meta.metaappton" submodule to
      "betse.util.app.meta.appmetaone".
    * "betse.util.io.log.logconfig" submodule to
      "betse.util.io.log.conf.logconf".
    * "betse.util.io.log.logformat" submodule to
      "betse.util.io.log.conf.logconfformat".
    * "betse.util.io.log.loghandle" submodule to
      "betse.util.io.log.conf.logconfhandle".
    * "betse_test.fixture.metaapper" submodule to
      "betse_test.fixture.initter".
  * Generalized the existing "betse.exceptions" submodule:
    * Defines new exception classes, including:
      * "BetseMappingKeyException".
      * "BetseLogRaceException", unique to the aforementioned log
        rotation race condition.
  * Generalized the existing "betse.metadata" submodule:
    * Replaced all references to the obsolete static "SCRIPT_BASENAME"
      global with the dynamic
      betse.util.app.meta.appmetaone.get_app_meta().package_name()
      property, which properly applies to downstream consumers (e.g.,
      BETSEE).
    * Renamed the "GIT_TAG_OLDEST_BACKWARD_COMPATIBILITY" global to
      "GIT_TAG_COMPAT_OLDEST" for brevity.
    * Reverted this global to "v0.5.0", restoring guaranteeable backward
      compatibility with all simulation configurations from BETSE >=
      0.5.0.
  * Generalized the existing "betse.lib.matplotlib.matplotlibs"
    submodule:
    * Defined a new MplConfig.logger() property, yielding Matplotlib's
      root logger object (i.e., "matplotlib").
  * Optimized the existing "betse.lib.numpy.numpys" submodule:
    * Defined a new _is_blas_optimized_conda() tester, deferring to the
      newly defined betse.util.py.pys.is_conda() tester.
    * Optimized the existing is_blas_optimized() tester to
      preferentially call the _is_blas_optimized_conda() tester first,
      which is guaranteed to be both the most optimal and portable
      solution.
  * Streamlined the existing "betse.lib.setuptools.command.supcmdtest"
    submodule:
    * Removed the obsolete "test.export_sim_conf_dir" instance variable.
  * Generalized the existing "betse.lib.yaml.yamls" submodule:
    * Refactored the load() function to accept a new optional
      "yaml_version" parameter, overriding any version directive
      prefacing the passed YAML-formatted file. When passed such a
      parameter with a value other than "1.1" (e.g., "1.2"), this
      function temporarily ignores all non-fatal warnings emitted by
      "ruamel.yaml" specific to the YAML 1.1 specification.
  * Defined a new "betse.lib.yaml.abc.yamlfileabc" submodule:
    * Shifted the prior "betse.lib.yaml.abc.yamlabc.YamlFileABC"
      superclass into this submodule:
      * Defined a new copy() method, encapsulating the lower-level
        load() and save() methods for safe copying YAML-formatted files.
      * Generalized the load() method to accept variadic keyword
        arguments, passed as is to the betse.lib.yaml.yamls.load()
        function.
      * Generalized the save() method to accept two new optional
        parameters:
        * "is_conf_file_overwritable", the overwrite policy for the
          passed target file.
        * "conf_subdir_overwrite_policy", the overwrite policy for
          subdirectories of this file.
    * Defined a new "YamlFileDefaultABC" superclass defining:
      * An abstract conf_default_filename() class property, returning
        the absolute filename of the subclass-specific default YAML file
        (e.g., default simulation configuration).
      * A concrete copy_default() method, copying this file to the
        passed target filename.
  * Removed the "betse.science.config.confio" submodule.
  * Generalized the existing "betse.science.parameters" submodule:
    * Refactored the "Parameters" class:
      * To subclass the newly defined "YamlFileDefaultABC" subclass.
    * Refactored the load() method to override any version directive
      prefacing all YAML-formatted files loaded by this method with
      "1.2", preserving backward compatibility with prior simulation
      configuration files erroneously prefaced by the "%YAML 1.1"
      directive.
    * Replaced all prior calls to the
      betse.science.config.confio.write_default() function with calls to
      the save_default() method.
  * Generalized the existing "betse.science.math.mesh" submodule:
    * Improved the DECMesh.refine_mesh() method to log rather than
      merely print informational messages.
  * Generalized the existing "betse.util.app.meta.metaappabc" submodule:
    * Refactored the "AppMetaABC" superclass:
      * Defined a new deinit() method, deinitializing both this
        application metadata singleton and the current application.
      * Defined the following new properties:
        * module_metadata(), the subclass-specific application metadata
          submodule.
        * module_metadeps(), the subclass-specific application
          dependency metadata submodule.
        * test_package(), the root test package for the current
          application if found or raises an exception otherwise.
        * test_dirname(), the absolute dirname of that package's
          directory if found or raises an exception otherwise.
        * test_data_dirname(), the absolute dirname of that package's
          data subdirectory if found or raises an exception otherwise.
      * Replaced all prior references to the prior abstract
        betse.util.cli.cliabc._module_metadata() property to the new
        module_metadata() property and removed the former.
  * Generalized the existing "betse.util.app.meta.appmetamod" submodule:
    * Defined a new merge_module_metadeps() function:
      * Dynamically creating and returning a new application dependency
        metadata module with arbitrary name iteratively merged from the
        contents of all passed application dependency metadata modules.
      * Copying the "RequirementCommand" class globally defined by the
        first such module into the output module.
  * Generalized the existing "betse.util.app.meta.appmetaone" submodule:
    * Defined a new deinit() function, conditionally encapsulating the
      lower-level betse.util.app.meta.appmetaabc.AppMetaABC.deinit()
      method.
  * Generalized the "betse.util.cli.cliabc" submodule:
    * Refactored the _make_options_top() method into an "_options_top"
      property, preserving orthogonality with the existing
      "betse.util.cli.clicmdabc._subcommander_top" property.
    * Refactored the "_is_option_matplotlib_backend" boolean property
      into an "_matplotlib_backend_name_forced" string property,
      enabling subclasses to force initialization of a desired
      subclass-specific matplotlib backend.
  * Defined a new "betse.util.io.log.conf" subpackage.
  * Defined a new "betse.util.io.log.conf.logconfcls" submodule:
    * Shifted the prior "betse.util.io.log.logconfig.LogConfig" class to
      "LogConf" in this submodule.
  * Generalized the existing "betse.util.io.log.conf.logconf" submodule:
    * Defined a new deinit() function calling a new
      betse.util.io.log.conf.logconfcls.LogConf.deinit() method,
      deinitializing the initialized logging configuration and hence
      closing all open file handles associated with this configuration.
    * Improved the existing init() function to reduce to a noop rather
      than reinitialize a previously initialized logging configuration.
  * Generalized the existing "betse.util.io.log.conf.logconfhandle"
    submodule:
    * Refactored the .LogHandlerFileRotateSafe" subclass:
      * Revised class and method docstrings for clarity, including a
        recommendation that external callers manually pass the
        "--log-file" CLI option when running concurrent BETSE processes.
      * Refactored the _emit_safely() method to:
        * Accept a "self" parameter, a critical bug fix.
        * Sleep for 100ms rather than 50ms.
        * Print additional debug output to standard error documenting
          this method's automatic resolution process.
        * Raise a human-readable exception on exhausting all other
          options, including the same recommendation as documented in
          docstrings.
  * Generalized the existing "betse.util.io.log.logs" submodule:
    * Renamed the get() function to get_logger() for disambiguity.
  * Generalized the existing "betse.util.io.stdouts" submodule:
    * Repaired the output_lines() function, which suffered a critical
      (albeit trivial) defect.
  * Defined a new "betse.util.os.brand.linux" submodule:
    * Defined a new is_mir() tester, returning true only if the active
      Python interpreter is running under the Mir compositor.
    * Defined a new is_wayland() tester, returning true only if the
      active Python interpreter is running under the Wayland compositor.
    * Shifted the existing betse.util.os.is_linux() tester into this
      submodule.
  * Defined a new "betse.util.os.brand.macos" submodule:
    * Shifted the existing betse.util.os.is_macos() tester into this
      submodule.
  * Defined a new "betse.util.os.brand.posix" submodule:
    * Defined a new is_x11() tester, returning true only if the active
      Python interpreter is running under an X11 display server.
    * Shifted the existing betse.util.os.is_posix() tester into this
      submodule.
  * Generalized the existing "betse.util.os.brand.windows" submodule:
    * Defined a new is_version_10_or_newer() tester, returning true if
      the current platform is Windows >= 10.
    * Defined a new get_api_version() getter, returning the current
      low-level Windows API (WinAPI, Win32) version.
    * Shifted the is_windows(), is_windows_cygwin(),
      is_windows_vanilla(), and is_windows_wsl() testers from the
      existing "betse.util.os.brand.os" submodule into this submodule.
  * Generalized the existing "betse.util.os.displays" submodule:
    * Defined a new is_dpi_scaling() tester, returning true if the
      current platform natively supports high-DPI scaling.
    * Refactored the is_headless() tester to defer to the most recent
      call to the newly defined set_headless() function (if any).
    * Defined a new set_headless() setter, enabling callers to
      explicitly force headless operation and hence override the
      implicit detection of headless environments performed by the
      is_headless() tester.
  * Generalized the existing "betse.util.os.kernels" submodule:
    * Defined a new is_version_greater_than_or_equal_to() tester,
      enabling callers to trivially detect minimum kernel versions and
      hence the existence of platform features introduced by those
      versions.
    * Revises the get_version() getter to return the high-level Windows
      kernel version (e.g., "10.0.10240") rather than the low-level
      Windows API version (e.g., "6.2.9200").
  * Generalized the existing "betse.util.os.shell.shellenv" submodule:
    * Defined a new to_str() function, returning a human-readable string
      of all environment variables defined by the host shell
      environment.
  * Generalized the existing "betse.util.py.module.pymodname" submodule:
    * Defined a new make_module() function, enabling callers to
      dynamically generate new in-memory modules containing arbitrary
      module attributes.
  * Generalized the existing "betse.util.py.module.pymodule" submodule:
    * Defining a new is_module() tester, returning true only if the
      passed object is a module.
  * Generalized the existing "betse.util.type.obj.objects" submodule:
    * Refactoring the get_attr() getter to embed the fully-qualified
      name of the passed module when passed a module in raised
      exceptions.
  * Generalized the existing "betse.util.py.pys" submodule:
    * Defined a new is_conda() tester, returning true if the active
      Python interpreter is managed by conda. For debuggability, this
      metadata is now logged when the "info" subcommand is run.
  * Defined a new "betse.util.test.pytest.fixture" subpackage:
    * Defined a new "betse.util.test.pytest.fixture.pytfixture"
      submodule:
      * Defined a new monkeypatch_session() fixture, generalizing the
        standard function-scope "monkeypatch" fixture to session scope.
  * Generalized the existing "betse.util.test.pytest.pytests" submodule:
    * Defined a new output() function, printing the passed objects to
      standard output in a format consistent with that of standard
      "py.test" messages.
  * Generalized the existing "betse.util.type.iterable.iterables"
    submodule:
    * Defined a new to_str() function, synthesizing a human-readable
      prettified string of the passed iterable.
    * Improves the existing to_iterable() converter to behave as
      expected when passed an input generator.
  * Defined a new "betse.util.type.iterable.iteriter" submodule:
    * Defined a new iter_pairs() iterator, creating and returning a new
      iterable of all possible pairs of items in the passed iterable.
    * Defined a new iter_combinations() iterator, creating and returning
      a new iterable of all possible k-combinations of items in the
      passed iterable.
  * Generalized the existing "betse.util.type.iterable.mapping.mappings"
    submodule:
    * Defined a new die_unless_keys_unique() function, raising an
      exception unless no passed dictionaries collide (i.e., contain the
      same key).
    * Defined a new is_keys_unique() tester, returning true only if no
      passed dictionaries collide.
    * Defined a new iter_keys_ascending() iterator, returning an
      iterable of all keys of the passed dictionary in ascending order
      (typically, lexicographic).
    * Removed the unused (and largely useless) format_map() function,
      now superceded by the newly defined
      betse.util.type.iterable.iterables.to_str() function.
  * Defined a new "betse.util.type.iterable.mapping.mapmerge" submodule:
    * Shifted the betse.util.type.iterable.mapping.mappings.merge_maps()
      function into this submodule.
    * Defined a new "MergeCollisionPolicy" enumeration type whose
      members each describe a different type of key collision merger
      policy (i.e., strategy for merging keys shared by one or more
      mappings).
    * Refactored the merge_maps() function to:
      * Merge arbitrarily many dictionaries under any key collision
        policy.
      * Behave as expected when passed a non-sequence of input mappings.
      * Defer to the newly defined
        betse.util.type.iterable.mapping.maptest.die_if_maps_collide()
        validator.
  * Defined a new "betse.util.type.iterable.mapping.maptest" submodule:
    * Shifted all testing and exception handling logic from the
      "betse.util.type.iterable.mapping.mappings" submodule into this
      submodule.
    * Renamed the following functions for orthogonality:
      * die_unless_keys_equal() to die_unless_maps_keys_equal().
      * die_unless_keys_unique() to die_unless_maps_keys_unique().
      * is_key() to has_keys().
      * is_keys_equal() to is_maps_keys_equal().
      * is_keys_unique() to is_maps_keys_unique().
    * Defined a new die_unless_has_keys() validator, raising an
      exception unless the passed mappings contains all passed keys.
    * Replaced the largely useless die_unless_maps_keys_unique()
      validator with a new die_if_maps_collide() validator, raising an
      exception only if no passed mappings collide.
    * Replaced the largely useless is_maps_keys_unique() tester with a
      new is_maps_collide() tester, returning true only if one or more
      passed mappings collide (i.e., contain key-value pairs containing
      the same keys but differing values).
    * Refactored the die_unless_maps_keys_unique() validator to improve
      the granularity of the exception message raised by this function,
      which now iteratively finds the exact set of key collisions in the
      passed dictionaries with the newly defined
      betse.util.type.iterable.iteriter.iter_pairs() iterator.
    * Refactored the has_keys() tester to accept a simple iterable of
      all keys to be tested rather than a variadic positional tuple of
      such keys.
  * Generalized the "betse.util.type.iterable.sequences" submodule:
    * Defined a new die_if_length_less_than() validator, raising an
      exception if the passed sequence contains less than the passed
      number of items.
    * Defined a new to_sequence() converter, converting the passed
      iterable into a sequence as needed.
    * Refactored the die_if_empty() validator to operate similarly to
      all other validators defined by this submodule.
  * Generalizes the "betse.util.type.iterable.mapping.maptest"
    submodule:
    * Refactored the is_maps_collide() tester to call the newly defined
      betse.util.type.iterable.sequences.die_if_length_less_than() and
      betse.util.type.iterable.set.sets.make_union() functions, thereby
      both simplifying and correcting the existing implementation.
  * Defined a new "betse.util.type.iterable.set" subpackage:
    * Shifted the existing "betse.util.type.iterable.sets" submodule to
      "betse.util.type.iterable.set.setcls" for disambiguity.
    * Defined a new "betse.util.type.iterable.set.sets" submodule:
      * Defined a new make_union() function, calculating the union of
        all items in arbitrarily many possibly non-set iterables.
  * Generalized the existing "betse.util.type.text.string.strs"
    submodule:
    * Refactored the get_prefix_preceding_char_or_none() getter:
      * Renamed this getter to get_prefix_or_none().
      * Renamed the "char" parameter accepted by this getter to
        "anchor".
      * Added an optional "is_first" parameter to this getter, enabling
        callers to retrieve both greedy and non-greedy prefixes.
  * Streamlined the existing "betse_setup.buputil" submodule by removing
    extraneous functionality (e.g., die_unless_module(), is_module()).
  * Streamlined the existing "betse_test.conftest" submodule:
    * Removed all functionality pertaining to the ${DISPLAY} environment
      variable from the less robust and reusable pytest_configure()
      function in favour of the
      betse_test.fixture.initter.betse_autouse() fixture.
    * Removed the obsolete "EXPORT_SIM_CONF_DIRNAME" global.
  * Defined a new "betse_test.data" subdirectory embedding the default
    simulation configuration supplied by BETSE 0.5.0, enabling the
    betse_test.func.sim.test_sim.test_cli_sim_compat() functional test
    to fully exercise backward compatibility with this configuration.
  * Rewrote the "betse_test.fixture.initter" submodule:
    * Defined a new autouse package-scoped betse_init_package() fixture,
      intended to be imported by the "betse_test.unit.conftest" plugin
      to guarantee application initialization before unit testing.
    * Renamed the betse_meta_app() fixture to betse_autouse(), which now
      additionally unsets the ${DISPLAY} environment variable if set.
    * Moved the existing betse_test.conftest._init_app() and
      _deinit_app() functions into this submodule as init_app() and
      deinit_app() for importability elsewhere.
  * Generalized the existing "betse_test.fixture.simconf.simconfer"
    submodule:
    * Redefined the betse_sim_conf() fixture in terms of the lower-level
      betse_sim_conf_default() fixture.
    * Refactored the betse_sim_conf_compat() fixture to leverage the
      Git-hosted "betse_test/data/v0.5.0/yaml/sim_config.yaml"
      simulation configuration corresponding to the default
      configuration circa BETSE 0.5.0.
  * Generalized the existing "betse_test.fixture.simconf.simconfclser"
    submodule:
    * Refactored the "SimConfTestABC" superclass to initialize the
      application metadata singleton before saving a simulation
      configuration for the current functional test.
    * Refactored the "SimConfTestInternal" subclass to require
      general-purpose "src_conf_filename" and "trg_conf_filepath"
      filenames be passed on initialization. Previously, this subclass
      only accepted the latter and defaulted the former to the default
      simulation configuration file.
  * Generalized the existing "betse_test.fixture.simconf.simconfwrapper"
    submodule:
    * Refactored the "SimConfigTestWrapper" class:
      * Refactored the __init__() method to accept a high-level
        "Parameters" object rather than a low-level simulation
        configuration filename.
      * Removed a number of extraneous methods and properties (notably,
        the make_default() class method).
  * Generalized the existing "betse_test.func.fixture.clier" submodule:
    * Called the betse.util.app.meta.metaappabc.AppMetaABC.deinit()
      method before running each functional test.
  * Removed the obsolete "betse_test.func.sim.test_sim_export"
    submodule.
  * Generalized the existing "betse_test.func.sim.test_sim" submodule:
    * Removed all @Skip decoration from the test_cli_sim_compat() test.
  * Defined a new "betse_test.unit.type.iterable.mapping.test_mapmerge"
    submodule, fully exercising the newly generalized
    betse.util.type.iterable.mapping.mapmerge.merge_maps() function (now
    featuring 667% more obscure Cthulhu mythos references).
  * Defined a new "betse_test.unit.app.meta.test_appmetamod" submodule,
    fully exercising the newly defined
    betse.util.app.meta.appmetamod.merge_module_metadeps() function.
  * Generalized the betse_test.unit.path.test_dirs.test_packages_init()
    unit test to exclude all data-specific directories from
    consideration, including both the "AppMetaABC.data_dirname" and
    "AppMetaABC.test_data_dirname" directories if found.
  * Generalized the existing
    betse_test.unit.type.iterable.test_iterables.test_to_iterable() unit
    test to exercise additional edge cases of that underlying function.

v1.0.0

BETSE 1.0.0 (Mighty Maxwell) released.

Significant changes include:

* Discrete exterior calculus (DEC). In the seed phase, BETSE now
  rigorously constructs the Voronoi diagrams and dual triangular meshes
  underlying all cell clusters according to DEC theory. DEC operators
  and methods are available for external use via the newly introduced
  "cells.mesh" object.
* New "fast" and "full" solver types. BETSE can now work from an
  equivalent circuit model formalism (e.g. Hodgkin-Huxley based model of
  bioelectricity) using the new "fast" solver or from its original
  molecular view of bioelectricity using the "full" solver type. This
  allows users to tailor their computational perspective to the needs of
  their biophysical problem.
* Expression profile read-in. BETSE can now read in expression levels
  from a user-defined expression configuration file for any
  custom-defined biomolecule, transporter, or channel. This allows
  different levels of a biomolecule, transporter, or channel to be
  specified on all of the user-defined tissue profiles of a BETSE model,
  where expression data can be sourced from experiments or experimental
  databases.
* Single cell simulation. BETSE can now simulate a single cell, a
  feature requested for fast prototyping of certain simulations.
* SVG model definition. In addition to the original bitmap method of
  defining cell cluster and tissue profiles, BETSE also allows use of
  Scalable Vector Graphics (SVG) files to define exact locations of
  cells in your cluster, with the fill colors of circles defined by
  these files now assigning the cell centred at that circle to the
  tissue profile associated with that color.
* Mesh refinement. Optionally run a refinement algorithm on BETSE meshes
  to generate more computationally robust model geometries.
* Modulator functions. Use a customizable modulator function (e.g.
  gradient along the x-axis) to modulate expression of custom
  biomolecules, transporters, or pumps.
* Significant bugs resolved, including:
  * Gene regulatory network (GRN) equilibrium guarantees. Reactions and
    transporter rate expressions in user-defined GRNs now converge to
    the appropriate equilibrium constants.
  * Arrowhead size in exported visuals sanitized. Arrowheads in exported
    stream plots and animations are now sized appropriately rather than
    oversized.
  * Cation leak channels.
* Deprecations and warnings resolved, including:
  * Deprecated usage of the "pytest.config" global. The
    betse_test.conftest.pytest_configure() hook now sets the
    "betse_test.conftest.EXPORT_SIM_CONF_DIRNAME" global to the value of
    the "--export-sim-conf-dirname" option, circumventing
    "pytest.config" entirely.
  * Deprecated usage of the yaml.load() function with no explicit
    "Loader". The betse.lib.yaml.yamls._load_pyyaml() function now
    dynamically calls whichever of the yaml.full_load() or yaml.load()
    functions are available for the current version of PyYAML, thus
    circumventing yaml.load() insecurities under newer PyYAML versions.
* Simulation configuration improvements, including:
  * Nameable exports. A human-readable name (e.g., "Cell Cluster:
    Transmembrane Voltage") may now be associated with each pipelined
    export, including plots, animations, and CSV-formatted files.
  * Obsolete settings removed, including:
    * A vestigial block of intervention-specific settings.
  * Data descriptor encapsulation. Various settings derived from the
    currently open YAML-formatted simulation configuration file have now
    been refactored from low-level instance variables of the
    "betse.science.parameters.Parameters" class to high-level
    yaml_alias()-based data descriptors, enabling downstream consumers
    (e.g., the BETSEE GUI) to transparently propagate modifications to
    these settings back to this file. These include settings pertaining
    to:
    * Enumeration (i.e., indexation, numbering) of cells in the cell
      cluster.
    * Matplotlib colormap names.
* Documentation improvements, including:
  * Tutorials. Official user- and developer-specific BETSE tutorials
    have now been added to the repository. A "Tutorials" subsection
    listing this documentation has also been the top-level "README.rst"
    file.
  * Simulation configuration settings, including:
    * Application-specific matplotlib colormaps. Our default
      YAML-formatted simulation configuration file now exhaustively
      lists the names of all available application-specific matplotlib
      colormaps.
* Test suite improvements, including:
  * "py.test" exit code. The top-level "test" script now correctly
    reports the exact exit code reported by the "py.test" test suite.
  * Additional unit tests, including:
    * The "betse_test.unit.type.iterable.test_iterables" submodule,
      which now exercises the overly intricate and hence fragile
      betse.util.type.iterable.iterables.to_iterable() function.
* Debuggability improvements, including:
  * Type validation debuggability. Wrapper functions synthesized by the
    @type_check decorator now sport the disambiguous name
    "__{func_name}_type_checked__" rather than the ambiguous name
    "func_type_checked".
* API generalizations, including:
  * The "betse.science.pipe" subpackage, such that:
    * The existing SimPipeABC.iter_runners_conf() method is now
      constrained to strictly return indexable sequences rather than
      possibly non-indexable iterables, as external callers commonly
      require the former.
    * The new SimPipeABC.iter_runners_metadata_kind() method returns a
      sequence of the types of all runners declared by the current
      pipeline (in sorted lexicographic order).
    * The prior "SimPipeRunnerMetadata.name" instance variable has been
      renamed to "SimPipeRunnerMetadata.kind" for disambiguity with the
      unrelated "name" data descriptor of runner subconfigurations.
  * The "betse.lib.matplotlib.mplcolormap" submodule, which defines:
    * The iter_colormap_names() iterator, yielding the names of all
      currently registered matplotlib colormaps (in sorted lexicographic
      order).
  * The "betse.util.math.geometry.geopoint" submodule, whose is_point()
    function has been generalized to test whether coordinates of each
    passed point are strictly numeric or not.
  * The "betse.util.type.iterable.iterables" submodule, whose:
    * to_iterable() function has been generalized to optionally accept
      an "item_cls" parameter, which when non-None coerces all items of
      the returned iterable to instances of that parameter.
  * The "betse.util.type.iterable.iterget" submodule, whose:
    * Newly defined get_item_first() and get_item_first_or_sentinel()
      functions retrieve the first items from arbitrarily many
      iterables.
    * get_item_str_uniquified() has been generalized to optionally
      implicitly synthesize default string values for non-compliant
      items, as needed for preserving backward compatibility.
  * The "betse.util.type.iterable.itersort" submodule, whose:
    * _sort_iterable() function has been optimized for the common case
      of generators and generator comprehensions.
  * The "betse.util.type.iterable.itertest" submodule, whose:
    * is_items_unique() function has been:
      * Generalized to support iterables not satisfying the
        "collections.abc.Sized" protocol (e.g., generators).
      * Optimized to induce best-case time complexity in the average
        case.
  * The "betse.util.type.iterable.mapping.mappings" submodule, which
    defines:
    * A pair of related die_unless_keys_equal() and is_keys_equal()
      functions, efficiently validating and testing whether the passed
      set of dictionaries all contain the exact same keys.
  * The "betse.util.type.iterable.sequences" submodule, which defines:
    * The get_index() function, enabling callers to retrieve items with
      arbitrary indices from arbitrary sequences in a manner guaranteed
      to raise human-readable exceptions on failure.
  * The "betse.util.type.obj.objects" submodule, whose:
    * get_method() function has been renamed to get_callable().
    * get_method_or_none() function has been renamed to
      get_callable_or_none().
    * "method_name" parameters accepted by both of the aforementioned
      functions have been renamed to "callable_name".
  * The "betse.util.type.text.string.strjoin" submodule, which defines:
    * A new join_iterable_as_conjunction_double_quoted() function
      implicitly coercing all items of the passed iterable to strings.
  * The "betse.util.type.types" submodule, which now defines:
    * The special "MethodWrapperType" type, formerly available only
      under Python >= 3.7 as "types.MethodWrapperType".
    * A "ContainerTypes" type supporting both standard Python containers
      and Numpy arrays, replacing the prior "ContainerType" type not
      supporting Numpy arrays.
    * A "SequenceStandardTypes" tuple of all standard sequence types
      (i.e., "list", "tuple").
    * Significantly fewer obsolete testers and asserters, which have now
      been permanently removed in favour of the @type_check decorator.

v0.9.2

BETSE 0.9.2 (Luckiest Levin) released.

Significant changes include:

* Downstream data dirname issues resolved. Previously, downstream
  consumers (e.g., the BETSEE GUI) were erroneously required to recreate
  rather than share the default simulation configuration provided by the
  BETSE codebase. This has now been resolved, reducing unseemly Don't
  Repeat Yourself (DRY).
* Gene regulatory network (GRN) pathname issues resolved. Specifically,
  the "gene regulatory network settings/gene regulatory network config"
  setting in YAML-formatted simulation configuration files has now been
  generalized to be relative to the directory containing those files.
* Resource consumption reduced. Matplotlib-based figures, graphs, plots,
  and animations are now properly destroyed on completion, substantially
  reducing memory usage during long-running simulation exports.
* Deprecation warnings eliminated. The codebase now complies with all
  upstream deprecations, including breaking NumPy, py.test, and Python
  changes. Notably, NumPy >= 1.16 has deprecated the passing of
  arbitrary iterables to the np.column_stack() function, which now
  requires strict sequences:
      FutureWarning: arrays to stack must be passed as a "sequence" type
      such as list or tuple. Support for non-sequence iterables such as
      generators is deprecated as of NumPy 1.16 and will raise an error
      in the future.
  Since converting non-sequence iterables to sequences is trivial
  (ignoring the obvious edge case of infinite generators, which much
  like the legendary Pokemon of yore appear not to exist in the wild),
  NumPy developers chose poorly when they chose to explicitly break
  rather than implicitly resolve this non-issue. Thanks for generating
  more meaningless boilerplate, NumPy.
* Significant continuous integration (CI) improvements, including:
  * Conda-forge-centric testing. All CI host environments (i.e.,
    GitLab-CI, AppVeyor) now install all mandatory and optional
    application dependencies from the open-source volunteer
    "conda-forge" channel rather than the closed-source proprietary
    "anaconda" channel. Doing so better mimics real-world installation
    environments and enables us to begin exercising optional
    dependencies (e.g., FFmpeg, NetworkX, PyDot).
  * Early-time logging verbosity increased. To simplify testing
    maintenance, Early-time logging verbosity increased has now been
    increased during tests.
  * CI-specific decorators added. The new
    betse.util.test.pytest.mark.pytskip.skip_if_ci_gitlab() decorator
    skips the decorated test or fixture if GitLab-CI is currently
    hosting these tests.
  * The following previously failing tests have been corrected:
    * The test_packages_init() unit test, which is now conditionally
      skipped under GitLab-CI by the aforementioned decorator. GitLab-CI
      no longer clones this repository correctly, erroneously preserving
      empty subdirectories no longer tracked by git. While usually
      innocuous, empty subdirectories trigger false negatives from this
      unit test. Since this test succeeds both locally and under
      AppVeyor, GitLab-CI's misconfigured git checkout policy
      inexplicably remains the culprit.
    * The test_c_* pair of unit tests, which erupted into incoherent
      flames due to inappropriate assumptions about the internal
      structure of NumPy. NumPy
      1.16.0 unified the prior public "numpy.core.multiarray" and
      "numpy.core.umath" C extensions into a new private
      "numpy.core._multiarray_umath" C extension.
    * The pivotal test_cli_sim_compat() functional test, which has now
      been reenabled in a manner seemingly incompatible with recent
      py.test changes. Since this test safeguards backward compatibility
      with respect to our YAML-based simulation configuration format,
      enabling this test is critical to long-term joy.
* Installation-time improvements, including:
  * Substantial "setup.py" streamlining. The contents of our topmost
    setuptools-based installation script now reflect those of their
    counterpart in the BETSEE GUI -- which is to say, this script has
    been compacted into a fairly minimal line length.
  * "betse.metadata.PYTHON_VERSION_MINOR_MAX" addition. This integer
    global specifies the maximum minor stable version of the Python 3.x
    mainline, as required by this "setup.py" streamlining.
  * "betse_setup.buputil" streamlining. This submodule collects
    miscellaneous utility functions required only at BETSE installation.
    This submodule has now been reduced in length, reducing our
    maintenance burden.
* Significant internal API improvements, including:
  * Application API creation. The new "betse.util.app" subpackage now
    provides:
    * The prior "betse.util.meta" subpackage, which has been subsumed
      under "betse.util.app.meta" for maintainability.
    * Application pathname API creation. The new
      "betse.util.app.apppath" submodule enables callers to query the
      absolute pathnames of application-relative paths regardless of
      application installation specifics (e.g., setuptools-based egg,
      frozen application).
    * Application initialization API centralized. The existing
      "betse.util.app.meta.metaappabc.MetaAppABC" superclass now
      centralizes all application initialization logic into a single
      coherent object-oriented API previously distributed throughout the
      codebase as a cacophony of incoherent procedural APIs not reusable
      by downstream consumers. This includes:
      * The "MetaAppABC" constructor, which now guarantees the current
        application to be completely initialized excluding third-party
        dependencies (e.g., NumPy, SciPy), whose initialization must
        necessarily be deferred until later in the application startup
        process. This constructor now entirely obsoletes the prior
        "betse.ignition" API.
      * The MetaAppABC.init_libs() method, which now initializes
        third-party dependencies (e.g., NumPy, SciPy) in a
        user-configurable manner late in the application startup
        process. This method now obsoletes a significant portion of the
        existing "betse.lib.libs" API.
      * The MetaAppABC.init_libs_if_needed() method, which efficiently
        simplifies the aforementioned process on behalf of low-level
        automation (e.g., dark voodoo in the "betse.science.__init__"
        module).
    * Application metadata singleton API creation. The new
      "betse.util.app.meta.metaappton" submodule centralizes all logic
      concerning the application metadata singleton previously residing
      in the "betse.metaapp" submodule.
  * Bitwise API creation. The new "betse.util.type.numeric.bits"
    submodule now provides general-purpose bit and bit field
    functionality, including an is_bit_on() function implementing a
    standard bit field testing routine.
  * Matplotlib Figure API creation. The new
    "betse.lib.matplotlib.mplfigure" submodule defines critical
    functionality for reliably closing matplotlib figures, a prevalent
    "pain point" in most matplotlib-based applications.
  * Testing API creation. The new "betse.util.test" subpackage now
    defines testing-specific APIs, including:
    * The new "betse.util.test.tests" submodule, defining
      general-purpose test suite functionality. This includes the
      is_testing() tester, enabling the codebase itself to query whether
      or not it is currently being tested.
    * The new "betse.util.test.tstci" submodule, defining CI-specific
      functionality. This includes one tester for each well-known CI
      host (e.g., is_ci_gitlab() for GitLab-CI), returning true only
      when the active Python interpreter is exercising tests on that
      host.
    * The new py.test API. The BETSE-specific and hence non-reusable
      "betse_test.util" subpackage has been moved to the BETSE-agnostic
      and hence reusable "betse.util.test.pytest" subpackage, enabling
      reuse by downstream consumers (e.g., BETSEE).
  * Class API generalizations. The "betse.util.type.cls.classes"
    submodule now defines a host of new general-purpose getter
    functions, including:
    * get_name_qualified(), obtaining the fully-qualified name of the
      passed class. The implementation proved highly non-trivial, due to
      common edge cases (e.g., classes with no or poorly defined
      modules).
    * get_module_name_qualified_or_none(), obtaining the fully-qualified
      name of the module defining the passed class if any or "None"
      otherwise.
  * Module API generalizations. A new "betse.util.py.module" subpackage
    has been created, replacing the prior "betse.util.py.pymodule"
    submodule. This subpackage provides the following new submodules:
    * "betse.util.py.module.pymodname", providing lookup facilities both
      to and from fully-qualified module names and their corresponding
      module objects.
    * "betse.util.py.module.pymodule", providing generic module
      handling.
    * "betse.util.py.module.pypackage", providing generic package
      handling -- notably, the get_object_type_package_root() function
      returning the root package transitively defining any arbitrary
      object's class.
  * String API generalizations. The existing "betse.util.type.text.strs"
    submodule now defines a holy trifecta of
    "get_prefix_preceding_char"-prefixed functions, enabling callers to
    obtain the prefix preceding any character in arbitrary strings.
* Obsolete subdirectories removed, including:
  * The long-broken "doc/examples" subdirectory, whose code snippets
    have all non-working for at least several major application
    revisions. While documentation in and of itself is commendable, bad
    documentation is readily worse than no documentation.
  * The long-unused "betse/data/yamale" subdirectory.

v0.9.1

BETSE 0.9.1 (Luckier Levin) released.

Significant changes include:

* Cell boundary search radius increased, improving simulation stability
  in worst-case spatial layouts for user-defined cell clusters.
* MetaAppABC API added. The new object-oriented "betse.metaapp"
  submodule replaces the prior procedural "betse.pathtree" submodule,
  enabling inheritance-fueled reuse from downstream consumers (e.g.,
  BETSEE). The MetaAppABC API is intended to eventually encompass all
  general-purpose functionality required.to implement BETSE-like
  applications -- including application initialization, dependency
  resolution and initialization, and a nominal command-line interface
  (CLI).
* Git API generalized. The new
  betse.util.path.gits.get_package_worktree_dirname_or_none() function
  queries whether a given top-level Python package is in Git-based
  development mode.
* Numpy API generalized. The new
  betse.lib.numpy.nparray.get_subarray_indices() function efficiently
  yields the indices of all items of one Numpy array residing in
  another.
* Path API generalized. The new high-level
  betse.util.files.copy_overwritable() function, wrapping the existing
  lower-level betse.util.files.copy() function for the common case of
  silently overwriting arbitrary target files.
* String API generalized to support substantially enhanced truncation
  operations in the "betse.util.type.text.strs" submodule, including:
  * The new high-level truncate() function, truncating strings to
    caller-defined barriers (e.g., maximum length, terminal substring)
    and then replacing the truncated suffixes with arbitrary strings
    (e.g., ellipses).
  * The new replace_suffix_prefixed() function, prematurely truncating
    strings on impermissible substrings (e.g., newline).
  * The existing remove_suffix_with_prefix() function reduced to a
    one-liner, a substantial optimization preventing this function from
    erroneously raising exceptions in edge cases both common and
    uncommon.
  * The existing
    betse.util.type.text.regexes.die_unless_replace_substrs_line()
    function, now truncating internally raised exception messages for
    readability.
* Version API generalized. The formerly mostly empty
  "betse.util.type.numeric.versions" submodule has been substantially
  improved, despite the inability of the setuptools-bundled
  "pkg_resources" API to preserve backward compatibility across stable
  releases. Our version API now portably allows versions in any of
  several formats (i.e., string, tuple, or setuptools-specific "Version"
  or "LegacyVersion" objects) to be reliably compared. PEP 440-compliant
  versions (e.g., "5.9.0") and PEP 440-uncompliant versions (e.g.,
  "5.9.0~a1") are explicitly supported.
* Version API tested. Unit tests fully exercising all edge cases of the
  "betse.util.type.numeric.versions" submodule have been added.