diff --git a/.gitignore b/.gitignore index a0097e73..d0938cd7 100644 --- a/.gitignore +++ b/.gitignore @@ -26,10 +26,7 @@ build/ *.bak0 *.bak1 sundials-*.tar.gz -.local/ -.ipython/ -.jupyter/* -!.jupyter/jupyter_notebook_config.py +.env/ tmp/ .coverage htmlcov/ diff --git a/README.rst b/README.rst index a16c85af..83f1d2e4 100644 --- a/README.rst +++ b/README.rst @@ -107,13 +107,13 @@ Using Docker If you have `Docker `_ installed, you may use it to host a jupyter notebook server:: - $ ./scripts/host-jupyter-using-docker.sh . 8888 + $ ./scripts/host-env.sh host-notebook --port 8888 the first time you run the command, some dependencies will be downloaded. When the installation is complete there will be a link visible which you can open in your browser. You can also run the test suite using the same docker-image:: - $ ./scripts/host-jupyter-using-docker.sh . 0 + $ ./scripts/host-env.sh run-tests there will be a few skipped test (due to some dependencies not being installed by default) and quite a few warnings. diff --git a/chempy/util/tests/test_graph.py b/chempy/util/tests/test_graph.py index 6f249cef..86df81c5 100644 --- a/chempy/util/tests/test_graph.py +++ b/chempy/util/tests/test_graph.py @@ -11,7 +11,7 @@ from ..testing import requires, skipif try: - dot_missing = subprocess.call(["dot", "-?"]) != 0 + dot_missing = subprocess.run(["dot", "-?"]).returncode != 0 except OSError: dot_missing = True diff --git a/scripts/environment/Dockerfile b/scripts/environment/Containerfile similarity index 52% rename from scripts/environment/Dockerfile rename to scripts/environment/Containerfile index aa55f98e..2271aed1 100644 --- a/scripts/environment/Dockerfile +++ b/scripts/environment/Containerfile @@ -1,19 +1,20 @@ -FROM docker.io/debian:bullseye +FROM docker.io/debian:bookworm MAINTAINER Björn Dahlgren ENV LANG C.UTF-8 RUN apt-get update && \ - apt-get --quiet --assume-yes install curl git g++-10 gfortran-10 libgmp-dev binutils-dev bzip2 make cmake sudo \ - python3-dev python3-pip libboost-dev libgsl-dev liblapack-dev libsuitesparse-dev graphviz && \ + apt-get --quiet --assume-yes install --no-install-recommends \ + curl git g++ gfortran libgmp-dev binutils-dev bzip2 make cmake sudo \ + python3-dev python3-venv python3-pip libboost-dev libgsl-dev liblapack-dev libsuitesparse-dev graphviz && \ apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* -RUN mkdir /tmp/sundials-5.5.0-build && \ - curl -Ls https://github.com/LLNL/sundials/releases/download/v5.5.0/sundials-5.5.0.tar.gz | tar xz -C /tmp && \ - FC=gfortran-10 cmake \ - -S /tmp/sundials-5.5.0 \ - -B /tmp/sundials-5.5.0-build \ +RUN mkdir /tmp/sundials-5.8.0-build && \ + curl -Ls https://github.com/LLNL/sundials/releases/download/v5.8.0/sundials-5.8.0.tar.gz | tar xz -C /tmp && \ + FC=gfortran cmake \ + -S /tmp/sundials-5.8.0 \ + -B /tmp/sundials-5.8.0-build \ -DCMAKE_INSTALL_PREFIX=/usr/local \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_SHARED_LIBS=ON \ @@ -25,15 +26,11 @@ RUN mkdir /tmp/sundials-5.5.0-build && \ -DENABLE_KLU=ON \ -DKLU_INCLUDE_DIR=/usr/include/suitesparse \ -DKLU_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu && \ - cmake --build /tmp/sundials-5.5.0-build && \ - cmake --build /tmp/sundials-5.5.0-build --target install && \ - rm -r /tmp/sundials-5.5.0*/ && \ - python3 -m pip install --upgrade-strategy=eager --upgrade pip && \ - python3 -m pip install --upgrade-strategy=eager numpy cython setuptools && \ + cmake --build /tmp/sundials-5.8.0-build && \ + cmake --build /tmp/sundials-5.8.0-build --target install && \ + rm -r /tmp/sundials-5.8.0*/ && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* -# http://computation.llnl.gov/projects/sundials/download/sundials-5.5.0.tar.gz - # At this point the system should be able to pip-install the package and all of its dependencies. We'll do so # when running the image using the ``host-jupyter-using-docker.sh`` script. Installed packages are cached. diff --git a/scripts/host-env.sh b/scripts/host-env.sh new file mode 100755 index 00000000..34092d0c --- /dev/null +++ b/scripts/host-env.sh @@ -0,0 +1,110 @@ +#!/bin/bash +show_help() { + echo "Usage:" + echo "$(basename $0) host-notebook --port 8888 --listen 127.0.0.1" + echo "$(basename $0) run-tests" +} +set -euxo pipefail + +HOST_NOTEBOOK=0 +RUN_TESTS=0 +PORT=8000 +while [ $# -gt 0 ]; do + case "$1" in + host-notebook) + HOST_NOTEBOOK=1 + shift + ;; + --port) + shift + PORT=$1 + shift + ;; + --listen) + shift + LISTEN=$1 + shift + ;; + run-tests) + RUN_TESTS=1 + shift + ;; + --help|-h) + show_help + exit 0 + ;; + --) + break; + ;; + *) + show_help + exit 1 + ;; + esac +done + +if ! which podrun >/dev/null; then + SOME_TEMP_DIR=$(mktemp -d) + trap 'rm -rf -- "$SOME_TEMP_DIR"' EXIT + ( cd "$SOME_TEMP_DIR"; curl -LOs https://raw.githubusercontent.com/bjodah/dotfiles/master/per-leaf-dir/bin/podrun; chmod +x podrun ) + export PATH="$SOME_TEMP_DIR:$PATH" +fi + +SCRIPTS_DIR=$(dirname $(realpath "$BASH_SOURCE")) +REPO_DIR=$(realpath "$SCRIPTS_DIR/..") +ENV_DIR="$REPO_DIR/.env" +PKG_NAME=${PKG_NAME:-$(basename $REPO_DIR)} + +mkdir -p $ENV_DIR + +cat <$ENV_DIR/setup.sh +if [ ! -d .env/ ]; then + >&2 echo "No .env directory?" + exit 1 +fi +if [ ! -d .env/venv ]; then + python3 -m venv .env/venv +fi +source .env/venv/bin/activate +if ! python -c "import pycvodes" 2>&1 >/dev/null; then + python -m pip install --upgrade-strategy=eager --upgrade pip && \ + python -m pip install --upgrade-strategy=eager numpy 'cython>=3.0.10' setuptools && \ + env \ + PYCVODES_NO_LAPACK=1 \ + PYCVODES_NO_KLU=1 \ + LDFLAGS='-Wl,--disable-new-dtags -Wl,-rpath,/usr/local/lib -L/usr/local/lib' \ + python -m pip install --cache-dir .env/pypi-cache -e .[all] +fi +if ! python -c "import pyodesys" 2>&1 >/dev/null; then + python -m pip install --cache-dir .env/pypi-cache -e .[all] + #jupyter-nbextension enable --user --py widgetsnbextension +fi +export MPLBACKEND=Agg +EOF + +cat <$ENV_DIR/run-tests.sh +#!/bin/bash +set -e +source .env/setup.sh +pytest -sv -ra --pyargs $PKG_NAME +EOF + +cat <$ENV_DIR/host-notebook.sh +#!/bin/bash +set -e +source .env/setup.sh +jupyter notebook --no-browser --port $PORT --ip=* index.ipynb +EOF + + +if [ $RUN_TESTS = 1 ]; then + podrun --cont-img-dir $SCRIPTS_DIR/environment \ + --name "${PKG_NAME}_run_tests" \ + -- bash $ENV_DIR/run-tests.sh +fi +if [ $HOST_NOTEBOOK = 1 ]; then + podrun --cont-img-dir $SCRIPTS_DIR/environment \ + --name "${PKG_NAME}_host_notebook_${PORT}" \ + -p $LISTEN:$PORT:$PORT \ + -- bash $ENV_DIR/host-notebook.sh +fi diff --git a/scripts/host-jupyter-using-docker.sh b/scripts/host-jupyter-using-docker.sh deleted file mode 100755 index b419f5fa..00000000 --- a/scripts/host-jupyter-using-docker.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash -ue -# -# This script requires that Docker (or podman) is installed. -# -# Arguments: mount-path, port-number, Dockerfile-path -# -# To host a local jupyter notebook server rung e.g.: -# -# $ ./scripts/host-jupyter-using-docker.sh -# $ ./scripts/host-jupyter-using-docker.sh . 8888 ./scripts/environment -# -# To instead run the test suite, specify "0" as the port number: -# -# $ ./scripts/host-jupyter-using-docker.sh . 0 ./scripts/environment -# -MOUNT=${1:-.} -PORT=${2:-8888} -DOCKERIMAGE=${3:-./scripts/environment} -PKG=$(find . -maxdepth 2 -name __init__.py -print0 | xargs -0 -n1 dirname | xargs basename) -HOST_USER=${SUDO_USER:-${LOGNAME}} -if [[ "${HOST_USER}" == root ]]; then - >&2 echo "Need another user name than root (pip will fail)" - exit 1 -fi - -if which podman; then - PODMAN=podman -elif which docker; then - if groups | grep docker; then - PODMAN=docker - else - PODMAN="sudo docker" - fi -else - >&2 echo "Neither podman nor docker found on \$PATH" - exit 1 -fi - -if [[ "$MOUNT" == .* ]]; then - MOUNT="$(pwd)/$MOUNT" -fi - - -if [[ "$DOCKERIMAGE" == ./* ]]; then - DOCKERIMAGE=$($PODMAN build $DOCKERIMAGE | tee /dev/tty | tail -1 | cut -d' ' -f3) -fi -if [[ "$PORT" == "0" ]]; then - LOCALCMD="pytest -sv -ra --pyargs $PKG" - PORTFWD="" -else - LOCALCMD="jupyter notebook --no-browser --port $PORT --ip=* index.ipynb" - PORTFWD="-p ${4:-127.0.0.1}:$PORT:$PORT" -fi -MYCMD="groupadd -f --gid \$HOST_GID \$HOST_WHOAMI; \ -useradd --uid \$HOST_UID --gid \$HOST_GID --home /mount \$HOST_WHOAMI; \ -sudo --login -u \$HOST_WHOAMI PYCVODES_NO_LAPACK=1 PYCVODES_NO_KLU=1 python3 -m pip install --user -e .[all]; \ -sudo --login -u \$HOST_WHOAMI /mount/.local/bin/jupyter-nbextension enable --user --py widgetsnbextension; \ -sudo --login -u \$HOST_WHOAMI LD_LIBRARY_PATH=/usr/local/lib MPLBACKEND=Agg /mount/.local/bin/$LOCALCMD" - -set -x - - -$PODMAN run \ - --rm \ - --name "${PKG}_nb_${PORT}" $PORTFWD \ - -e HOST_WHOAMI=${HOST_USER} \ - -e HOST_UID=$(id -u ${HOST_USER}) \ - -e HOST_GID=$(id -g ${HOST_USER}) \ - -v $MOUNT:/mount -w /mount -it $DOCKERIMAGE /bin/bash -x -c "$MYCMD" diff --git a/setup.py b/setup.py index 9793e47b..5c0bfb27 100755 --- a/setup.py +++ b/setup.py @@ -100,7 +100,7 @@ def _path_under_setup(*args): _author, _author_email = open(_path_under_setup("AUTHORS"), "rt").readline().split("<") extras_req = { - "integrators": ["pyodeint>=0.10.4", "pycvodes>=0.14.0", "pygslodeiv2>=0.9.4"], + "integrators": ["pyodeint>=0.10.4", "pycvodes>=0.14.5", "pygslodeiv2>=0.9.4"], "solvers": ["pykinsol>=0.1.6"], "native": ["pycompilation>=0.4.12", "pycodeexport>=0.1.3", "appdirs"], "docs": ["Sphinx", "sphinx_rtd_theme", "numpydoc"], @@ -128,7 +128,7 @@ def _path_under_setup(*args): "sympy>=1.1.1,!=1.2", "quantities>=0.12.1", "pyneqsys>=0.5.5", - "pyodesys>=0.14.1" if sys.version_info[0] >= 3 else "pyodesys<0.12", + "pyodesys>=0.14.4" if sys.version_info[0] >= 3 else "pyodesys<0.12", "pyparsing>=2.0.3", "sym>=0.3.4", "pulp>=1.6.8",