diff --git a/.github/ISSUE_TEMPLATE/new-package.md b/.github/ISSUE_TEMPLATE/new-package.md new file mode 100644 index 0000000..6ed3097 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/new-package.md @@ -0,0 +1,29 @@ +--- +name: New Package +about: Request a new python package and/or system library be installed +title: "Request a New Package" +labels: 'new-package' + +--- + +Please fill in the fields below to request new packages for the OpenSAFELY python image. + + +### Python package(s) you wish to add to the image + +<!-- please add packages, including links to pypi.org page if possible --> + + +### System Libraries + +<! --Any system libraries that this package may require. Leave blank if unsure --> + + +### Requesting Project + +<!-- Link to the OpenSAFELY Project that will use these packages --> + + +### Rationale + +<!-- Rationale for use in OpenSAFELY --> diff --git a/.github/workflows/build_and_publish.yaml b/.github/workflows/build_and_publish.yaml index 84349d8..ad4161e 100644 --- a/.github/workflows/build_and_publish.yaml +++ b/.github/workflows/build_and_publish.yaml @@ -1,33 +1,34 @@ name: Build and publish on: + workflow_dispatch: push: branches: [main] - workflow_dispatch: -permissions: - packages: write -env: - IMAGE_NAME: python + jobs: - build-and-publish: - runs-on: ubuntu-20.04 + publish: + # note: this builds/tests all versions in serial for two reasons. Firstly we + # want all versions to release or none of them. Secondly, we will be able + # publish the exact images that were built and tested. + runs-on: ubuntu-22.04 steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Build image - run: make build + - uses: actions/checkout@v3 + - uses: "opensafely-core/setup-action@v1" + with: + install-just: true + - name: Build images + run: | + just build v1 + just build v2 - name: Run tests - run: make test functional-test - - name: Run lint - run: make lint + run: | + just test v1 + just test v2 + - name: Run linters + run: just check + - name: Log into GitHub Container Registry run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login https://ghcr.io -u ${{ github.actor }} --password-stdin - name: Push image to GitHub Container Registry run: | - IMAGE_ID="ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME" - docker tag "$IMAGE_NAME" "$IMAGE_ID:latest" - docker push "$IMAGE_ID:latest" - - JUPYTER_ID="ghcr.io/${{ github.repository_owner }}/jupyter" - # also publish as jupyter image for backward compatibility - docker tag "$IMAGE_NAME" "$JUPYTER_ID:latest" - docker push "$JUPYTER_ID:latest" + just publish v1 true + just publish v2 true diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index b449bd5..f5a9ed6 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -1,17 +1,28 @@ name: Run tests on: pull_request: -env: - IMAGE_NAME: python jobs: - tests: - runs-on: ubuntu-20.04 + version-tests: + runs-on: ubuntu-22.04 + strategy: + matrix: + version: [v1, v2] steps: - - name: Checkout - uses: actions/checkout@master + - uses: actions/checkout@v3 + - uses: "opensafely-core/setup-action@v1" + with: + install-just: true - name: Build image - run: make build + run: just build ${{ matrix.version }} - name: Run tests - run: make test functional-test - - name: Run lint - run: make lint + run: just test ${{ matrix.version }} + lint: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: "opensafely-core/setup-action@v1" + with: + install-just: true + - name: Run linters + run: just check + diff --git a/DEVELOPERS.md b/DEVELOPERS.md new file mode 100644 index 0000000..1c9deea --- /dev/null +++ b/DEVELOPERS.md @@ -0,0 +1,71 @@ +# Basics + +Each major version has its configuration in a subdirectory named after the +version, e.g. ./v1/ has all the configuration for the `v1` image. + +Inside each version's directory there are 4 main files: + +- `env`: environment variables used to parameterise the Docker/docker-compose + files: + - `BASE`: the base Ubuntu version to build from, e.g. `22.04` + - `MAJOR_VERSION`: this shoud match the directory name. +- `dependencies.txt`: the Ubuntu packages that need to be installed +- `build-dependencies.txt`: the Ubuntu package needed to *build* any + dependencies (these will *not* be included in the final image). +- `requirements.in`: the list of packages to install (*without* version + specfiers, unless needed for some reason). + +There will also be two autogenerated files: + +- `requirements.txt`: the fully pinned set of python dependences generated with + `pip-compile`. +- `packages.md`: generated user facing documentation of package versions + + +Use just to build and test image versions: + +``` +just build v2 +just test v2 +``` + + +## Add a new package to existing version + +* Add the new package without version specifier to all relevant version's + `requirement.in` files +* For each version, do the following: + * Run `just update $VERSION`. This will update pacakges, then build and + test the new image. + * If the build fails, depending on the error message: + - you may need to add a new system package to `dependencies.txt` + - you may need to add a new build dependency package to + `build-dependencies.txt` + - you may need to finesse the tests for poorly packaged libraries: see + [`BAD_PACKAGES`](./tests/test_import.py) + * Inspect the changes to requirements.txt + - ensure no pre-existing package has been updated by this change. + + +## Create a new version + +TODO, but basically, `cp -a v$N v${N+1}` and edit. + + +## Publishing + +> ![WARNING] +> By default, these images are published via CI, so only do this if you know +> you need to, e.g. testing publishing a new version + +To publish a version locally, you will need to be logged in to ghcr.io with the +right permissions (`docker login ghcr.io`) + +By default, this command is a dry run, and will show you the commands it *will* run: + +`just publish $version` + +To run for real, pass `true`: + +`just publish $version true` + diff --git a/Dockerfile b/Dockerfile index 6de34b7..8bdf3af 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,61 +8,77 @@ # and b) we specifically always want to build on the latest base image, by # design. # +ARG BASE # hadolint ignore=DL3007 -FROM ghcr.io/opensafely-core/base-action:latest as base-python -COPY dependencies.txt /root/dependencies.txt +FROM ghcr.io/opensafely-core/base-action:$BASE as base-python + +RUN mkdir /workspace +WORKDIR /workspace + +ARG MAJOR_VERSION +ARG BASE +# ACTION_EXEC sets the default executable for the entrypoint in the base-docker image +ENV ACTION_EXEC=python MAJOR_VERSION=${MAJOR_VERSION} BASE=${BASE} + +COPY ${MAJOR_VERSION}/dependencies.txt /opt/dependencies.txt # use space efficient utility from base image -RUN /root/docker-apt-install.sh /root/dependencies.txt +RUN /root/docker-apt-install.sh /opt/dependencies.txt + +# now we have python, set up a venv to install packages to, for isolation from +# system python libraries +# hadolint ignore=DL3059 +RUN python3 -m venv /opt/venv +# "activate" the venv +ENV VIRTUAL_ENV=/opt/venv/ PATH="/opt/venv/bin:$PATH" +# We ensure up-to-date build tools (which why we ignore DL3013) +# hadolint ignore=DL3013,DL3042 +RUN --mount=type=cache,target=/root/.cache python -m pip install -U pip setuptools wheel pip-tools + ################################################# # # Next, use the base-docker-plus-python image to create a build image FROM base-python as builder +ARG MAJOR_VERSION # install build time dependencies -COPY build-dependencies.txt /root/build-dependencies.txt -RUN /root/docker-apt-install.sh /root/build-dependencies.txt - -# install everything in venv for isolation from system python libraries -# hadolint ignore=DL3059 -RUN python3 -m venv /opt/venv -ENV VIRTUAL_ENV=/opt/venv/ PATH="/opt/venv/bin:$PATH" LLVM_CONFIG=/usr/bin/llvm-config-10 +COPY ${MAJOR_VERSION}/build-dependencies.txt /opt/build-dependencies.txt +RUN /root/docker-apt-install.sh /opt/build-dependencies.txt -COPY requirements.txt /root/requirements.txt -# We ensure up-to-date build tools (which why we ignore DL3013) +COPY ${MAJOR_VERSION}/requirements.txt /opt/requirements.txt +COPY ${MAJOR_VERSION}/packages.md /opt/packages.md # Note: the mount command does two things: 1) caches across builds to speed up # local development and 2) ensures the pip cache does not get committed to the # layer (which is why we ignore DL3042). -# hadolint ignore=DL3013,DL3042 +# hadolint ignore=DL3042 RUN --mount=type=cache,target=/root/.cache \ - python -m pip install -U pip setuptools wheel && \ - python -m pip install --requirement /root/requirements.txt + python -m pip install --requirement /opt/requirements.txt ################################################ # # Finally, build the actual image from the base-python image FROM base-python as python + +ARG MAJOR_VERSION # Some static metadata for this specific image, as defined by: # https://github.com/opencontainers/image-spec/blob/master/annotations.md#pre-defined-annotation-keys # The org.opensafely.action label is used by the jobrunner to indicate this is # an approved action image to run. -LABEL org.opencontainers.image.title="python" \ +LABEL org.opencontainers.image.title="python:${MAJOR_VERSION}" \ org.opencontainers.image.description="Python action for opensafely.org" \ org.opencontainers.image.source="https://github.com/opensafely-core/python-docker" \ - org.opensafely.action="python" + org.opensafely.action="python:${MAJOR_VERSION}" # copy venv over from builder image -COPY --from=builder /opt/venv /opt/venv -# ACTION_EXEC sets the default executable for the entrypoint in the base-docker image -ENV VIRTUAL_ENV=/opt/venv/ PATH="/opt/venv/bin:$PATH" ACTION_EXEC=python - -RUN mkdir /workspace -WORKDIR /workspace +COPY --from=builder /opt/ /opt/ -# tag with build info as the very last step, as it will never be cached +# tag with build info as the very last step, as it will never be cacheable ARG BUILD_DATE ARG REVISION +ARG BUILD_NUMBER # RFC 3339. LABEL org.opencontainers.image.created=$BUILD_DATE \ - org.opencontainers.image.revision=$REVISION + org.opencontainers.image.revision=$REVISION \ + org.opencontainers.image.build=$BUILD_NUMBER \ + org.opencontainers.image.version=$MAJOR_VERSION.$BUILD_NUMBER diff --git a/Makefile b/Makefile deleted file mode 100644 index 3816bc8..0000000 --- a/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -INTERACTIVE:=$(shell [ -t 0 ] && echo 1) -export DOCKER_BUILDKIT=1 -export BUILD_DATE=$(shell date +'%y-%m-%dT%H:%M:%S.%3NZ') -export REVISION=$(shell git rev-parse --short HEAD) - -.PHONY: build -build: - docker-compose build --pull python - - -.PHONY: test -test: - docker-compose run --rm -v $(PWD):/workspace python pytest tests -v - -# test basic python invocation -functional-test: - docker-compose run --rm python -c '' - docker-compose run --rm python python -c '' - - -.PHONY: lint -lint: - @docker pull hadolint/hadolint:v2.8.0 - @docker run --rm -i hadolint/hadolint:v2.8.0 < Dockerfile - -requirements.txt: requirements.in venv/bin/pip-compile - venv/bin/pip-compile requirements.in - -venv/bin/pip-compile: | venv - venv/bin/pip install pip-tools - -venv: - virtualenv -p python3 venv - - diff --git a/README.md b/README.md index ba68d92..19fe6f9 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,57 @@ -# OpenSAFELY Python image +# OpenSAFELY Python Runtime Image -This is a dockerfile for running python in an OpenSAFELY -environment. +This repo manages the Docker image for the OpenSAFELY Python runtime. These +images are based on a base Ubuntu LTS version, and come pre-installed with +a set of standard scientific python packages. -To make a new release, use conventional commits to bump version -(i.e. subject lines starting `feat:` or `fix:`) +The current latest version is `v2`, and you should use that unless you have +a specific reason. You can use it in your `project.yaml` like so: -See `requirements.txt` for current available packages and versions +``` +actions: + my_action: + run: python:v2 my_script.py ... +``` + +## Version List + +Current available versions, in reverse chronological order: + + - v2: Ubuntu 22.04 and Python 3.10 - [full package list](v2/packages.md) + - v1: Ubuntu 20.04 and Python 3.8 - [full package list](v1/packages.md) + +### Legacy version: `latest` + +Initially, OpenSAFELY only had one version of the python image. This is the +`v1` image, but was originally published under the `:latest` tag. You can use +either `v1` or `latest` - they are the same version. In future, we may +deprecate the `latest` tag and require users to update their `project.yaml` to +use `v1` instead of `latest`. + + +## Update Policy + +### Python Package Versions + +For each version of the python image, we do *not* upgade the python packages +from their initially installed version. This is done in order to backwards +compatiblity and thus ensure reproduciblity. We do [add new packages on user +request](https://github.com/opensafely-core/python-dockerissues/new?template=new-package.md), +as such a change will not break backwards incompatibilty. + +Occasionally, we will create a new major version of the image with all packages +updated to their latest version. We may also possibly remove old and uneeded +pacakges at this point. A new major version is chance to make backwards +incompatible changes, which is occasionally needed. + +Once this new version of the image is intially released, its set of package +versions will be frozen and no longer updatable. + +### Operating System Packages + +We *do* update the underlying operating system packages on a regular basis, in +order to apply security updates to the base system. It is very unlikely that +this will break backwards compatibility, as these are a small set very +conservative set of updates to address security issues. + +We also add additional operating system libraries on user request. diff --git a/docker-compose.yml b/docker-compose.yml index a9751fa..e9f4f17 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,18 +1,26 @@ services: - # used to build the production image - python: - image: python + base: + init: true + image: python:${MAJOR_VERSION}-base build: context: . - target: python + target: base-python cache_from: # should speed up the build in CI, where we have a cold cache - - ghcr.io/opensafely-core/base-docker - - ghcr.io/opensafely-core/python + - ghcr.io/opensafely-core/base-action:${BASE} + - ghcr.io/opensafely-core/python:${MAJOR_VERSION} args: # this makes the image work for later cache_from: usage - BUILDKIT_INLINE_CACHE=1 - # env vars supplied by make/just + # env vars supplied by just + - BUILD_NUMBER - BUILD_DATE - REVISION - - VERSION - init: true + - BASE + - MAJOR_VERSION + + python: + extends: + service: base + image: python:${MAJOR_VERSION} + build: + target: python diff --git a/justfile b/justfile new file mode 100644 index 0000000..2b05e3f --- /dev/null +++ b/justfile @@ -0,0 +1,38 @@ +export DOCKER_BUILDKIT := "1" +# technically, these could differ by 1 seconds, but thats unlikely and doesn't matter +# human readable, used as label in docker image +export BUILD_DATE := `date +'%y-%m-%dT%H:%M:%S.%3NZ'` +# monotonic, used as label in docker image *and* in docker tag +export BUILD_NUMBER := `date +'%y%m%d%H%M%S'` +export REVISION := `git rev-parse --short HEAD` + +# build docker image for version +build version target="python" *args="": + docker compose --env-file {{ version }}/env build --pull {{ args }} {{ target }} + + +# test docker image for version +test version *args="tests -v": (build version) + docker compose --env-file {{ version }}/env run --rm -v $PWD:/workspace python pytest {{ args }} + + +# run pip-compile to add new dependencies, or update existing ones with --upgrade +update version *args="": + docker compose --env-file {{ version }}/env run --rm -v $PWD:/workspace base pip-compile {{ args }} {{ version }}/requirements.in -o {{ version }}/requirements.txt + {{ just_executable() }} render {{ version }} + {{ just_executable() }} test {{ version }} + +# render package version information +render version *args: + docker compose --env-file {{ version }}/env run --rm -v $PWD:/workspace python ./scripts/render.py {{ args }} > {{ version }}/packages.md + + +# run linters +check: + @docker pull hadolint/hadolint:v2.12.0 + @docker run --rm -i hadolint/hadolint:v2.12.0 < Dockerfile + + +# publish version (dry run by default - pass "true" to perform publish) +publish version publish="false": + PUBLISH={{ publish }} ./scripts/publish.sh {{ version }} diff --git a/scripts/packages.j2.md b/scripts/packages.j2.md new file mode 100644 index 0000000..e9dd3e1 --- /dev/null +++ b/scripts/packages.j2.md @@ -0,0 +1,12 @@ +# Package Versions for {{ MAJOR_VERSION }} + +This python:{{ MAJOR_VERSION }} OpenSAFELY image is based on Ubuntu {{ BASE }} with Python {{ PYTHON_VERSION }}. + +## Packages + +It comes pre-installed with a standard set of python packages. + +{% for pkg in PACKAGES -%} + - [{{ pkg | replace("==", ": ")}}](https://pypi.org/project/{{pkg.name}}/{{pkg.specs[0][1]}}/) +{% endfor -%} + diff --git a/scripts/publish.sh b/scripts/publish.sh new file mode 100755 index 0000000..19c8027 --- /dev/null +++ b/scripts/publish.sh @@ -0,0 +1,35 @@ +#!/bin/bash +set -euo pipefail + +version=$1 +registry=ghcr.io/opensafely-core + +run() { + echo "$@" + if test "${PUBLISH:-}" = "true"; then + # shellcheck disable=SC2068 + $@ + fi +} + +publish() { + local local_tag=$1; + local remote_tag=$2; + + run docker tag "$local_tag" "$remote_tag" + run docker push "$remote_tag" +} + +full_version="$(docker inspect --format='{{ index .Config.Labels "org.opencontainers.image.version"}}' "python:$version")" + +publish "python:$version" "$registry/python:$version" +publish "python:$version" "$registry/python:${full_version}" + +if test "$version" = "v1"; then + # jupyter is only alias for v1 + publish "python:$version" "$registry/jupyter:$version" + + # v1 is also known as latest, at least until we transition fully + publish "python:$version" "$registry/python:latest" + publish "python:$version" "$registry/jupyter:latest" +fi diff --git a/scripts/render.py b/scripts/render.py new file mode 100755 index 0000000..f036bfe --- /dev/null +++ b/scripts/render.py @@ -0,0 +1,24 @@ +#!/usr/bin/env -S python3 -W ignore +from pathlib import Path +import os +import sys +import pkg_resources + +from jinja2 import Environment, FileSystemLoader +env = Environment(loader=FileSystemLoader("scripts")) + +version = os.environ["MAJOR_VERSION"] +requirements = Path(version) / "requirements.txt" + +context = { + "MAJOR_VERSION": version, + "BASE": os.environ["BASE"], + "PYTHON_VERSION": "{}.{}.{}".format(*sys.version_info), +} + +with requirements.open() as r: + context["PACKAGES"] = list(pkg_resources.parse_requirements(r)) + +template = env.get_template("packages.j2.md") + +print(template.render(**context)) diff --git a/tests/test_import.py b/tests/test_import.py index 09d4a4a..f877650 100644 --- a/tests/test_import.py +++ b/tests/test_import.py @@ -1,21 +1,44 @@ +import os import subprocess from importlib import import_module from pathlib import Path +import re import pytest from pkg_resources import Requirement, get_provider +# packages that have no way to detect their importable name +BAD_PACKAGES = { + "beautifulsoup4": "bs4", + "protobuf": None, # AARRRRGG + "qtpy": None, # required dependency of jupyter-lab +} + def get_module_names(pkg_name): """Load pkg metadata to find out its importable module name(s).""" + # remove any extras + pkg_name = re.sub(r'\[.*\]', '', pkg_name) modules = set() provider = get_provider(Requirement.parse(pkg_name)) # top level package name is typically all we need - if provider.has_metadata("top_level.txt"): - modules |= set(provider.get_metadata_lines("top_level.txt")) + if pkg_name in BAD_PACKAGES: + name = BAD_PACKAGES[pkg_name] + if name is None: # unimportably package + return [] + modules.add(BAD_PACKAGES[pkg_name]) + elif provider.has_metadata("top_level.txt"): + first_line = list(provider.get_metadata_lines("top_level.txt"))[0] + modules.add(first_line) else: # badly packaged dependency, make an educated guess - modules.add(pkg_name.replace("-", "_")) + name = pkg_name + if pkg_name.endswith("-cffi"): + name = pkg_name[:-5] + elif pkg_name.endswith("-py"): + name = pkg_name[:-3] + + modules.add(name.replace("-", "_")) if provider.has_metadata("namespace_packages.txt"): modules |= set(provider.get_metadata_lines("namespace_packages.txt")) @@ -24,8 +47,9 @@ def get_module_names(pkg_name): return [n for n in modules if n[0] != "_"] -def generate_import_names(req_path): +def generate_import_names(major_version): """Generate list of expected modules to be able to import.""" + req_path = Path(major_version) / "requirements.txt" with req_path.open() as fp: for line in fp: line = line.strip() @@ -38,7 +62,7 @@ def generate_import_names(req_path): @pytest.mark.parametrize( - "name, module", generate_import_names(Path("requirements.txt")) + "name, module", generate_import_names(os.environ["MAJOR_VERSION"]) ) @pytest.mark.filterwarnings("ignore") def test_import_package(name, module): diff --git a/build-dependencies.txt b/v1/build-dependencies.txt similarity index 100% rename from build-dependencies.txt rename to v1/build-dependencies.txt diff --git a/dependencies.txt b/v1/dependencies.txt similarity index 100% rename from dependencies.txt rename to v1/dependencies.txt diff --git a/v1/env b/v1/env new file mode 100644 index 0000000..ea122d4 --- /dev/null +++ b/v1/env @@ -0,0 +1,2 @@ +MAJOR_VERSION=v1 +BASE=20.04 diff --git a/v1/packages.md b/v1/packages.md new file mode 100644 index 0000000..e578dc2 --- /dev/null +++ b/v1/packages.md @@ -0,0 +1,145 @@ +# Package Versions for v1 + +This python:v1 OpenSAFELY image is based on Ubuntu 20.04 with Python 3.8.10. + +## Packages + +It comes pre-installed with a standard set of python packages. + +- [astor: 0.8.1](https://pypi.org/project/astor/0.8.1/) +- [attrs: 19.3.0](https://pypi.org/project/attrs/19.3.0/) +- [autograd: 1.3](https://pypi.org/project/autograd/1.3/) +- [autograd-gamma: 0.5.0](https://pypi.org/project/autograd-gamma/0.5.0/) +- [backcall: 0.1.0](https://pypi.org/project/backcall/0.1.0/) +- [bash-kernel: 0.7.2](https://pypi.org/project/bash-kernel/0.7.2/) +- [bleach: 3.1.2](https://pypi.org/project/bleach/3.1.2/) +- [cachetools: 4.0.0](https://pypi.org/project/cachetools/4.0.0/) +- [cairocffi: 1.4.0](https://pypi.org/project/cairocffi/1.4.0/) +- [cairosvg: 2.5.2](https://pypi.org/project/cairosvg/2.5.2/) +- [certifi: 2019.11.28](https://pypi.org/project/certifi/2019.11.28/) +- [cffi: 1.15.1](https://pypi.org/project/cffi/1.15.1/) +- [chardet: 3.0.4](https://pypi.org/project/chardet/3.0.4/) +- [click: 7.0](https://pypi.org/project/click/7.0/) +- [click-plugins: 1.1.1](https://pypi.org/project/click-plugins/1.1.1/) +- [cligj: 0.5.0](https://pypi.org/project/cligj/0.5.0/) +- [coverage: 4.5.4](https://pypi.org/project/coverage/4.5.4/) +- [cssselect2: 0.7.0](https://pypi.org/project/cssselect2/0.7.0/) +- [cycler: 0.10.0](https://pypi.org/project/cycler/0.10.0/) +- [decorator: 4.4.1](https://pypi.org/project/decorator/4.4.1/) +- [defusedxml: 0.6.0](https://pypi.org/project/defusedxml/0.6.0/) +- [descartes: 1.1.0](https://pypi.org/project/descartes/1.1.0/) +- [ebmdatalab: 0.0.30](https://pypi.org/project/ebmdatalab/0.0.30/) +- [entrypoints: 0.3](https://pypi.org/project/entrypoints/0.3/) +- [fiona: 1.8.13](https://pypi.org/project/fiona/1.8.13/) +- [formulaic: 0.2.4](https://pypi.org/project/formulaic/0.2.4/) +- [future: 0.18.2](https://pypi.org/project/future/0.18.2/) +- [geopandas: 0.6.3](https://pypi.org/project/geopandas/0.6.3/) +- [google-api-core: 1.16.0](https://pypi.org/project/google-api-core/1.16.0/) +- [google-auth: 1.11.0](https://pypi.org/project/google-auth/1.11.0/) +- [google-auth-oauthlib: 0.4.1](https://pypi.org/project/google-auth-oauthlib/0.4.1/) +- [google-cloud-bigquery: 1.24.0](https://pypi.org/project/google-cloud-bigquery/1.24.0/) +- [google-cloud-core: 1.3.0](https://pypi.org/project/google-cloud-core/1.3.0/) +- [google-resumable-media: 0.5.0](https://pypi.org/project/google-resumable-media/0.5.0/) +- [googleapis-common-protos: 1.51.0](https://pypi.org/project/googleapis-common-protos/1.51.0/) +- [idna: 2.8](https://pypi.org/project/idna/2.8/) +- [interface-meta: 1.2.4](https://pypi.org/project/interface-meta/1.2.4/) +- [ipykernel: 5.1.4](https://pypi.org/project/ipykernel/5.1.4/) +- [ipython: 7.12.0](https://pypi.org/project/ipython/7.12.0/) +- [ipython-genutils: 0.2.0](https://pypi.org/project/ipython-genutils/0.2.0/) +- [ipywidgets: 7.5.1](https://pypi.org/project/ipywidgets/7.5.1/) +- [jedi: 0.16.0](https://pypi.org/project/jedi/0.16.0/) +- [jinja2: 2.11.1](https://pypi.org/project/jinja2/2.11.1/) +- [joblib: 1.0.1](https://pypi.org/project/joblib/1.0.1/) +- [json5: 0.9.0](https://pypi.org/project/json5/0.9.0/) +- [jsonschema: 3.2.0](https://pypi.org/project/jsonschema/3.2.0/) +- [jupyter: 1.0.0](https://pypi.org/project/jupyter/1.0.0/) +- [jupyter-client: 5.3.4](https://pypi.org/project/jupyter-client/5.3.4/) +- [jupyter-console: 6.1.0](https://pypi.org/project/jupyter-console/6.1.0/) +- [jupyter-core: 4.6.1](https://pypi.org/project/jupyter-core/4.6.1/) +- [jupyterlab: 1.2.6](https://pypi.org/project/jupyterlab/1.2.6/) +- [jupyterlab-server: 1.0.6](https://pypi.org/project/jupyterlab-server/1.0.6/) +- [jupytext: 1.3.3](https://pypi.org/project/jupytext/1.3.3/) +- [kaleido: 0.2.1](https://pypi.org/project/kaleido/0.2.1/) +- [kiwisolver: 1.1.0](https://pypi.org/project/kiwisolver/1.1.0/) +- [lifelines: 0.26.4](https://pypi.org/project/lifelines/0.26.4/) +- [llvmlite: 0.34.0](https://pypi.org/project/llvmlite/0.34.0/) +- [lz4: 3.1.3](https://pypi.org/project/lz4/3.1.3/) +- [markupsafe: 1.1.1](https://pypi.org/project/markupsafe/1.1.1/) +- [matplotlib: 3.1.3](https://pypi.org/project/matplotlib/3.1.3/) +- [mistune: 0.8.4](https://pypi.org/project/mistune/0.8.4/) +- [more-itertools: 8.2.0](https://pypi.org/project/more-itertools/8.2.0/) +- [munch: 2.5.0](https://pypi.org/project/munch/2.5.0/) +- [nbconvert: 5.6.1](https://pypi.org/project/nbconvert/5.6.1/) +- [nbformat: 5.0.4](https://pypi.org/project/nbformat/5.0.4/) +- [nbval: 0.9.4](https://pypi.org/project/nbval/0.9.4/) +- [notebook: 6.0.3](https://pypi.org/project/notebook/6.0.3/) +- [numba: 0.51.2](https://pypi.org/project/numba/0.51.2/) +- [numpy: 1.18.1](https://pypi.org/project/numpy/1.18.1/) +- [oauthlib: 3.1.0](https://pypi.org/project/oauthlib/3.1.0/) +- [opensafely-cohort-extractor: 1.88.0](https://pypi.org/project/opensafely-cohort-extractor/1.88.0/) +- [opensafely-matching: 0.2.0](https://pypi.org/project/opensafely-matching/0.2.0/) +- [packaging: 20.1](https://pypi.org/project/packaging/20.1/) +- [pandas: 1.0.1](https://pypi.org/project/pandas/1.0.1/) +- [pandas-gbq: 0.13.0](https://pypi.org/project/pandas-gbq/0.13.0/) +- [pandocfilters: 1.4.2](https://pypi.org/project/pandocfilters/1.4.2/) +- [parso: 0.6.1](https://pypi.org/project/parso/0.6.1/) +- [patsy: 0.5.1](https://pypi.org/project/patsy/0.5.1/) +- [pep517: 0.10.0](https://pypi.org/project/pep517/0.10.0/) +- [pexpect: 4.8.0](https://pypi.org/project/pexpect/4.8.0/) +- [pickleshare: 0.7.5](https://pypi.org/project/pickleshare/0.7.5/) +- [pillow: 8.1.0](https://pypi.org/project/pillow/8.1.0/) +- [pip-tools: 6.2.0](https://pypi.org/project/pip-tools/6.2.0/) +- [plotly: 4.5.0](https://pypi.org/project/plotly/4.5.0/) +- [pluggy: 0.13.1](https://pypi.org/project/pluggy/0.13.1/) +- [prometheus-client: 0.7.1](https://pypi.org/project/prometheus-client/0.7.1/) +- [prompt-toolkit: 3.0.3](https://pypi.org/project/prompt-toolkit/3.0.3/) +- [protobuf: 3.11.3](https://pypi.org/project/protobuf/3.11.3/) +- [ptyprocess: 0.6.0](https://pypi.org/project/ptyprocess/0.6.0/) +- [py: 1.8.1](https://pypi.org/project/py/1.8.1/) +- [pyarrow: 3.0.0](https://pypi.org/project/pyarrow/3.0.0/) +- [pyasn1: 0.4.8](https://pypi.org/project/pyasn1/0.4.8/) +- [pyasn1-modules: 0.2.8](https://pypi.org/project/pyasn1-modules/0.2.8/) +- [pycparser: 2.21](https://pypi.org/project/pycparser/2.21/) +- [pydata-google-auth: 0.3.0](https://pypi.org/project/pydata-google-auth/0.3.0/) +- [pygments: 2.5.2](https://pypi.org/project/pygments/2.5.2/) +- [pyparsing: 2.4.6](https://pypi.org/project/pyparsing/2.4.6/) +- [pyproj: 2.4.2.post1](https://pypi.org/project/pyproj/2.4.2.post1/) +- [pyrsistent: 0.15.7](https://pypi.org/project/pyrsistent/0.15.7/) +- [pytest: 5.3.5](https://pypi.org/project/pytest/5.3.5/) +- [python-dateutil: 2.8.1](https://pypi.org/project/python-dateutil/2.8.1/) +- [pytz: 2019.3](https://pypi.org/project/pytz/2019.3/) +- [pyyaml: 5.3](https://pypi.org/project/pyyaml/5.3/) +- [pyzmq: 18.1.1](https://pypi.org/project/pyzmq/18.1.1/) +- [qtconsole: 4.6.0](https://pypi.org/project/qtconsole/4.6.0/) +- [requests: 2.22.0](https://pypi.org/project/requests/2.22.0/) +- [requests-oauthlib: 1.3.0](https://pypi.org/project/requests-oauthlib/1.3.0/) +- [retry: 0.9.2](https://pypi.org/project/retry/0.9.2/) +- [retrying: 1.3.3](https://pypi.org/project/retrying/1.3.3/) +- [rsa: 4.0](https://pypi.org/project/rsa/4.0/) +- [scikit-learn: 0.24.1](https://pypi.org/project/scikit-learn/0.24.1/) +- [scipy: 1.4.1](https://pypi.org/project/scipy/1.4.1/) +- [seaborn: 0.10.0](https://pypi.org/project/seaborn/0.10.0/) +- [send2trash: 1.5.0](https://pypi.org/project/send2trash/1.5.0/) +- [shapely: 1.7.0](https://pypi.org/project/shapely/1.7.0/) +- [six: 1.14.0](https://pypi.org/project/six/1.14.0/) +- [sqlparse: 0.4.1](https://pypi.org/project/sqlparse/0.4.1/) +- [statsmodels: 0.11.0](https://pypi.org/project/statsmodels/0.11.0/) +- [structlog: 20.2.0](https://pypi.org/project/structlog/20.2.0/) +- [tabulate: 0.8.7](https://pypi.org/project/tabulate/0.8.7/) +- [terminado: 0.8.3](https://pypi.org/project/terminado/0.8.3/) +- [testpath: 0.4.4](https://pypi.org/project/testpath/0.4.4/) +- [threadpoolctl: 2.1.0](https://pypi.org/project/threadpoolctl/2.1.0/) +- [tinycss2: 1.2.1](https://pypi.org/project/tinycss2/1.2.1/) +- [toml: 0.10.2](https://pypi.org/project/toml/0.10.2/) +- [tornado: 6.0.3](https://pypi.org/project/tornado/6.0.3/) +- [tqdm: 4.42.1](https://pypi.org/project/tqdm/4.42.1/) +- [traitlets: 4.3.3](https://pypi.org/project/traitlets/4.3.3/) +- [upsetplot: 0.6.1](https://pypi.org/project/upsetplot/0.6.1/) +- [urllib3: 1.25.8](https://pypi.org/project/urllib3/1.25.8/) +- [venn: 0.1.3](https://pypi.org/project/venn/0.1.3/) +- [wcwidth: 0.1.8](https://pypi.org/project/wcwidth/0.1.8/) +- [webencodings: 0.5.1](https://pypi.org/project/webencodings/0.5.1/) +- [wheel: 0.36.2](https://pypi.org/project/wheel/0.36.2/) +- [widgetsnbextension: 3.5.1](https://pypi.org/project/widgetsnbextension/3.5.1/) +- [wrapt: 1.13.3](https://pypi.org/project/wrapt/1.13.3/) + diff --git a/requirements.in b/v1/requirements.in similarity index 100% rename from requirements.in rename to v1/requirements.in diff --git a/requirements.txt b/v1/requirements.txt similarity index 87% rename from requirements.txt rename to v1/requirements.txt index 18935e4..4b4b48a 100644 --- a/requirements.txt +++ b/v1/requirements.txt @@ -1,8 +1,8 @@ # -# This file is autogenerated by pip-compile with python 3.8 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.8 +# by the following command: # -# pip-compile +# pip-compile --output-file=v1/requirements.txt v1/requirements.in # astor==0.8.1 # via formulaic @@ -20,7 +20,7 @@ autograd-gamma==0.5.0 backcall==0.1.0 # via ipython bash-kernel==0.7.2 - # via -r requirements.in + # via -r v1/requirements.in bleach==3.1.2 # via nbconvert cachetools==4.0.0 @@ -28,7 +28,7 @@ cachetools==4.0.0 cairocffi==1.4.0 # via cairosvg cairosvg==2.5.2 - # via -r requirements.in + # via -r v1/requirements.in certifi==2019.11.28 # via requests cffi==1.15.1 @@ -37,6 +37,8 @@ chardet==3.0.4 # via requests click==7.0 # via + # click-plugins + # cligj # fiona # pip-tools click-plugins==1.1.1 @@ -61,7 +63,7 @@ defusedxml==0.6.0 descartes==1.1.0 # via ebmdatalab ebmdatalab==0.0.30 - # via -r requirements.in + # via -r v1/requirements.in entrypoints==0.3 # via nbconvert fiona==1.8.13 @@ -120,7 +122,7 @@ ipython-genutils==0.2.0 # traitlets ipywidgets==7.5.1 # via - # -r requirements.in + # -r v1/requirements.in # jupyter jedi==0.16.0 # via ipython @@ -139,7 +141,7 @@ jsonschema==3.2.0 # jupyterlab-server # nbformat jupyter==1.0.0 - # via -r requirements.in + # via -r v1/requirements.in jupyter-client==5.3.4 # via # ipykernel @@ -157,17 +159,17 @@ jupyter-core==4.6.1 # notebook # qtconsole jupyterlab==1.2.6 - # via -r requirements.in + # via -r v1/requirements.in jupyterlab-server==1.0.6 # via jupyterlab jupytext==1.3.3 - # via -r requirements.in + # via -r v1/requirements.in kaleido==0.2.1 - # via -r requirements.in + # via -r v1/requirements.in kiwisolver==1.1.0 # via matplotlib lifelines==0.26.4 - # via -r requirements.in + # via -r v1/requirements.in llvmlite==0.34.0 # via numba lz4==3.1.3 @@ -176,7 +178,7 @@ markupsafe==1.1.1 # via jinja2 matplotlib==3.1.3 # via - # -r requirements.in + # -r v1/requirements.in # descartes # lifelines # seaborn @@ -200,7 +202,7 @@ nbformat==5.0.4 # nbval # notebook nbval==0.9.4 - # via -r requirements.in + # via -r v1/requirements.in notebook==6.0.3 # via # jupyter @@ -208,10 +210,10 @@ notebook==6.0.3 # jupyterlab-server # widgetsnbextension numba==0.51.2 - # via -r requirements.in + # via -r v1/requirements.in numpy==1.18.1 # via - # -r requirements.in + # -r v1/requirements.in # autograd # formulaic # lifelines @@ -227,14 +229,14 @@ numpy==1.18.1 oauthlib==3.1.0 # via requests-oauthlib opensafely-cohort-extractor==1.88.0 - # via -r requirements.in + # via -r v1/requirements.in opensafely-matching==0.2.0 - # via -r requirements.in + # via -r v1/requirements.in packaging==20.1 # via pytest pandas==1.0.1 # via - # -r requirements.in + # -r v1/requirements.in # ebmdatalab # formulaic # geopandas @@ -247,7 +249,7 @@ pandas==1.0.1 # upsetplot pandas-gbq==0.13.0 # via - # -r requirements.in + # -r v1/requirements.in # ebmdatalab pandocfilters==1.4.2 # via nbconvert @@ -265,12 +267,12 @@ pickleshare==0.7.5 # via ipython pillow==8.1.0 # via - # -r requirements.in + # -r v1/requirements.in # cairosvg pip-tools==6.2.0 - # via -r requirements.in + # via -r v1/requirements.in plotly==4.5.0 - # via -r requirements.in + # via -r v1/requirements.in pluggy==0.13.1 # via pytest prometheus-client==0.7.1 @@ -283,6 +285,7 @@ protobuf==3.11.3 # via # google-api-core # google-cloud-bigquery + # googleapis-common-protos ptyprocess==0.6.0 # via # pexpect @@ -293,7 +296,7 @@ py==1.8.1 # retry pyarrow==3.0.0 # via - # -r requirements.in + # -r v1/requirements.in # opensafely-cohort-extractor pyasn1==0.4.8 # via @@ -321,7 +324,7 @@ pyrsistent==0.15.7 # via jsonschema pytest==5.3.5 # via - # -r requirements.in + # -r v1/requirements.in # nbval python-dateutil==2.8.1 # via @@ -356,10 +359,10 @@ retrying==1.3.3 rsa==4.0 # via google-auth scikit-learn==0.24.1 - # via -r requirements.in + # via -r v1/requirements.in scipy==1.4.1 # via - # -r requirements.in + # -r v1/requirements.in # autograd-gamma # formulaic # lifelines @@ -384,11 +387,13 @@ six==1.14.0 # google-cloud-bigquery # google-resumable-media # jsonschema + # munch # nbval # packaging # patsy # plotly # protobuf + # pyrsistent # python-dateutil # retrying # traitlets @@ -420,7 +425,7 @@ tornado==6.0.3 # notebook # terminado tqdm==4.42.1 - # via -r requirements.in + # via -r v1/requirements.in traitlets==4.3.3 # via # ipykernel @@ -433,11 +438,11 @@ traitlets==4.3.3 # notebook # qtconsole upsetplot==0.6.1 - # via -r requirements.in + # via -r v1/requirements.in urllib3==1.25.8 # via requests venn==0.1.3 - # via -r requirements.in + # via -r v1/requirements.in wcwidth==0.1.8 # via # prompt-toolkit diff --git a/v2/build-dependencies.txt b/v2/build-dependencies.txt new file mode 100644 index 0000000..d5c2de8 --- /dev/null +++ b/v2/build-dependencies.txt @@ -0,0 +1,10 @@ +# build time dependencies +build-essential +gcc +python3-dev +python3-venv +python3-wheel +# for numba +#llvm-10-dev +# for cairosvg +libffi-dev diff --git a/v2/dependencies.txt b/v2/dependencies.txt new file mode 100644 index 0000000..9fb4f06 --- /dev/null +++ b/v2/dependencies.txt @@ -0,0 +1,15 @@ +# run time dependencies +# ensure fully working base python3 installation +# see: https://gist.github.com/tiran/2dec9e03c6f901814f6d1e8dad09528e +python3 +python3-venv +python3-pip +python3-distutils +tzdata +ca-certificates + +# for cairosvg +libcairo2 + +# Some jupyter notebooks rely on using git to get current version of repo +git diff --git a/v2/env b/v2/env new file mode 100644 index 0000000..7cd7324 --- /dev/null +++ b/v2/env @@ -0,0 +1,2 @@ +MAJOR_VERSION=v2 +BASE=22.04 diff --git a/v2/packages.md b/v2/packages.md new file mode 100644 index 0000000..b9d3e05 --- /dev/null +++ b/v2/packages.md @@ -0,0 +1,162 @@ +# Package Versions for v2 + +This python:v2 OpenSAFELY image is based on Ubuntu 22.04 with Python 3.10.12. + +## Packages + +It comes pre-installed with a standard set of python packages. + +- [anyio: 4.1.0](https://pypi.org/project/anyio/4.1.0/) +- [argon2-cffi: 23.1.0](https://pypi.org/project/argon2-cffi/23.1.0/) +- [argon2-cffi-bindings: 21.2.0](https://pypi.org/project/argon2-cffi-bindings/21.2.0/) +- [arrow: 1.3.0](https://pypi.org/project/arrow/1.3.0/) +- [astor: 0.8.1](https://pypi.org/project/astor/0.8.1/) +- [asttokens: 2.4.1](https://pypi.org/project/asttokens/2.4.1/) +- [async-lru: 2.0.4](https://pypi.org/project/async-lru/2.0.4/) +- [attrs: 23.1.0](https://pypi.org/project/attrs/23.1.0/) +- [autograd: 1.6.2](https://pypi.org/project/autograd/1.6.2/) +- [autograd-gamma: 0.5.0](https://pypi.org/project/autograd-gamma/0.5.0/) +- [babel: 2.13.1](https://pypi.org/project/babel/2.13.1/) +- [bash-kernel: 0.9.3](https://pypi.org/project/bash-kernel/0.9.3/) +- [beautifulsoup4: 4.12.2](https://pypi.org/project/beautifulsoup4/4.12.2/) +- [bleach: 6.1.0](https://pypi.org/project/bleach/6.1.0/) +- [build: 1.0.3](https://pypi.org/project/build/1.0.3/) +- [cairocffi: 1.6.1](https://pypi.org/project/cairocffi/1.6.1/) +- [cairosvg: 2.7.1](https://pypi.org/project/cairosvg/2.7.1/) +- [certifi: 2023.11.17](https://pypi.org/project/certifi/2023.11.17/) +- [cffi: 1.16.0](https://pypi.org/project/cffi/1.16.0/) +- [charset-normalizer: 3.3.2](https://pypi.org/project/charset-normalizer/3.3.2/) +- [click: 8.1.7](https://pypi.org/project/click/8.1.7/) +- [comm: 0.2.0](https://pypi.org/project/comm/0.2.0/) +- [contourpy: 1.2.0](https://pypi.org/project/contourpy/1.2.0/) +- [coverage: 7.3.2](https://pypi.org/project/coverage/7.3.2/) +- [cssselect2: 0.7.0](https://pypi.org/project/cssselect2/0.7.0/) +- [cycler: 0.12.1](https://pypi.org/project/cycler/0.12.1/) +- [debugpy: 1.8.0](https://pypi.org/project/debugpy/1.8.0/) +- [decorator: 5.1.1](https://pypi.org/project/decorator/5.1.1/) +- [defusedxml: 0.7.1](https://pypi.org/project/defusedxml/0.7.1/) +- [exceptiongroup: 1.2.0](https://pypi.org/project/exceptiongroup/1.2.0/) +- [executing: 2.0.1](https://pypi.org/project/executing/2.0.1/) +- [fastjsonschema: 2.19.0](https://pypi.org/project/fastjsonschema/2.19.0/) +- [fonttools: 4.46.0](https://pypi.org/project/fonttools/4.46.0/) +- [formulaic: 0.6.6](https://pypi.org/project/formulaic/0.6.6/) +- [fqdn: 1.5.1](https://pypi.org/project/fqdn/1.5.1/) +- [future: 0.18.3](https://pypi.org/project/future/0.18.3/) +- [idna: 3.6](https://pypi.org/project/idna/3.6/) +- [iniconfig: 2.0.0](https://pypi.org/project/iniconfig/2.0.0/) +- [interface-meta: 1.3.0](https://pypi.org/project/interface-meta/1.3.0/) +- [ipykernel: 6.27.1](https://pypi.org/project/ipykernel/6.27.1/) +- [ipython: 8.18.1](https://pypi.org/project/ipython/8.18.1/) +- [ipywidgets: 8.1.1](https://pypi.org/project/ipywidgets/8.1.1/) +- [isoduration: 20.11.0](https://pypi.org/project/isoduration/20.11.0/) +- [jedi: 0.19.1](https://pypi.org/project/jedi/0.19.1/) +- [jinja2: 3.1.2](https://pypi.org/project/jinja2/3.1.2/) +- [joblib: 1.3.2](https://pypi.org/project/joblib/1.3.2/) +- [json5: 0.9.14](https://pypi.org/project/json5/0.9.14/) +- [jsonpointer: 2.4](https://pypi.org/project/jsonpointer/2.4/) +- [jsonschema[format-nongpl]: 4.20.0](https://pypi.org/project/jsonschema/4.20.0/) +- [jsonschema-specifications: 2023.11.2](https://pypi.org/project/jsonschema-specifications/2023.11.2/) +- [jupyter-client: 8.6.0](https://pypi.org/project/jupyter-client/8.6.0/) +- [jupyter-console: 6.6.3](https://pypi.org/project/jupyter-console/6.6.3/) +- [jupyter-core: 5.5.0](https://pypi.org/project/jupyter-core/5.5.0/) +- [jupyter-events: 0.9.0](https://pypi.org/project/jupyter-events/0.9.0/) +- [jupyter-lsp: 2.2.1](https://pypi.org/project/jupyter-lsp/2.2.1/) +- [jupyter-server: 2.11.2](https://pypi.org/project/jupyter-server/2.11.2/) +- [jupyter-server-terminals: 0.4.4](https://pypi.org/project/jupyter-server-terminals/0.4.4/) +- [jupyterlab: 4.0.9](https://pypi.org/project/jupyterlab/4.0.9/) +- [jupyterlab-pygments: 0.3.0](https://pypi.org/project/jupyterlab-pygments/0.3.0/) +- [jupyterlab-server: 2.25.2](https://pypi.org/project/jupyterlab-server/2.25.2/) +- [jupyterlab-widgets: 3.0.9](https://pypi.org/project/jupyterlab-widgets/3.0.9/) +- [jupytext: 1.16.0](https://pypi.org/project/jupytext/1.16.0/) +- [kaleido: 0.2.1](https://pypi.org/project/kaleido/0.2.1/) +- [kiwisolver: 1.4.5](https://pypi.org/project/kiwisolver/1.4.5/) +- [lifelines: 0.27.8](https://pypi.org/project/lifelines/0.27.8/) +- [llvmlite: 0.41.1](https://pypi.org/project/llvmlite/0.41.1/) +- [lz4: 4.3.2](https://pypi.org/project/lz4/4.3.2/) +- [markdown-it-py: 3.0.0](https://pypi.org/project/markdown-it-py/3.0.0/) +- [markupsafe: 2.1.3](https://pypi.org/project/markupsafe/2.1.3/) +- [matplotlib: 3.8.2](https://pypi.org/project/matplotlib/3.8.2/) +- [matplotlib-inline: 0.1.6](https://pypi.org/project/matplotlib-inline/0.1.6/) +- [mdit-py-plugins: 0.4.0](https://pypi.org/project/mdit-py-plugins/0.4.0/) +- [mdurl: 0.1.2](https://pypi.org/project/mdurl/0.1.2/) +- [mistune: 3.0.2](https://pypi.org/project/mistune/3.0.2/) +- [nbclient: 0.9.0](https://pypi.org/project/nbclient/0.9.0/) +- [nbconvert: 7.12.0](https://pypi.org/project/nbconvert/7.12.0/) +- [nbformat: 5.9.2](https://pypi.org/project/nbformat/5.9.2/) +- [nbval: 0.10.0](https://pypi.org/project/nbval/0.10.0/) +- [nest-asyncio: 1.5.8](https://pypi.org/project/nest-asyncio/1.5.8/) +- [notebook: 7.0.6](https://pypi.org/project/notebook/7.0.6/) +- [notebook-shim: 0.2.3](https://pypi.org/project/notebook-shim/0.2.3/) +- [numba: 0.58.1](https://pypi.org/project/numba/0.58.1/) +- [numpy: 1.26.2](https://pypi.org/project/numpy/1.26.2/) +- [opensafely-cohort-extractor: 1.90.0](https://pypi.org/project/opensafely-cohort-extractor/1.90.0/) +- [opensafely-matching: 0.2.0](https://pypi.org/project/opensafely-matching/0.2.0/) +- [overrides: 7.4.0](https://pypi.org/project/overrides/7.4.0/) +- [packaging: 23.2](https://pypi.org/project/packaging/23.2/) +- [pandas: 2.1.3](https://pypi.org/project/pandas/2.1.3/) +- [pandocfilters: 1.5.0](https://pypi.org/project/pandocfilters/1.5.0/) +- [parso: 0.8.3](https://pypi.org/project/parso/0.8.3/) +- [pexpect: 4.9.0](https://pypi.org/project/pexpect/4.9.0/) +- [pillow: 10.1.0](https://pypi.org/project/pillow/10.1.0/) +- [pip-tools: 7.3.0](https://pypi.org/project/pip-tools/7.3.0/) +- [platformdirs: 4.1.0](https://pypi.org/project/platformdirs/4.1.0/) +- [plotly: 5.18.0](https://pypi.org/project/plotly/5.18.0/) +- [pluggy: 1.3.0](https://pypi.org/project/pluggy/1.3.0/) +- [prometheus-client: 0.19.0](https://pypi.org/project/prometheus-client/0.19.0/) +- [prompt-toolkit: 3.0.41](https://pypi.org/project/prompt-toolkit/3.0.41/) +- [psutil: 5.9.6](https://pypi.org/project/psutil/5.9.6/) +- [ptyprocess: 0.7.0](https://pypi.org/project/ptyprocess/0.7.0/) +- [pure-eval: 0.2.2](https://pypi.org/project/pure-eval/0.2.2/) +- [py: 1.11.0](https://pypi.org/project/py/1.11.0/) +- [pyarrow: 14.0.1](https://pypi.org/project/pyarrow/14.0.1/) +- [pycparser: 2.21](https://pypi.org/project/pycparser/2.21/) +- [pygments: 2.17.2](https://pypi.org/project/pygments/2.17.2/) +- [pyparsing: 3.1.1](https://pypi.org/project/pyparsing/3.1.1/) +- [pyproject-hooks: 1.0.0](https://pypi.org/project/pyproject-hooks/1.0.0/) +- [pytest: 7.4.3](https://pypi.org/project/pytest/7.4.3/) +- [python-dateutil: 2.8.2](https://pypi.org/project/python-dateutil/2.8.2/) +- [python-json-logger: 2.0.7](https://pypi.org/project/python-json-logger/2.0.7/) +- [pytz: 2023.3.post1](https://pypi.org/project/pytz/2023.3.post1/) +- [pyyaml: 6.0.1](https://pypi.org/project/pyyaml/6.0.1/) +- [pyzmq: 25.1.2](https://pypi.org/project/pyzmq/25.1.2/) +- [referencing: 0.31.1](https://pypi.org/project/referencing/0.31.1/) +- [requests: 2.31.0](https://pypi.org/project/requests/2.31.0/) +- [retry: 0.9.2](https://pypi.org/project/retry/0.9.2/) +- [rfc3339-validator: 0.1.4](https://pypi.org/project/rfc3339-validator/0.1.4/) +- [rfc3986-validator: 0.1.1](https://pypi.org/project/rfc3986-validator/0.1.1/) +- [rpds-py: 0.13.2](https://pypi.org/project/rpds-py/0.13.2/) +- [scikit-learn: 1.3.2](https://pypi.org/project/scikit-learn/1.3.2/) +- [scipy: 1.11.4](https://pypi.org/project/scipy/1.11.4/) +- [seaborn: 0.13.0](https://pypi.org/project/seaborn/0.13.0/) +- [send2trash: 1.8.2](https://pypi.org/project/send2trash/1.8.2/) +- [six: 1.16.0](https://pypi.org/project/six/1.16.0/) +- [sniffio: 1.3.0](https://pypi.org/project/sniffio/1.3.0/) +- [soupsieve: 2.5](https://pypi.org/project/soupsieve/2.5/) +- [sqlparse: 0.4.4](https://pypi.org/project/sqlparse/0.4.4/) +- [stack-data: 0.6.3](https://pypi.org/project/stack-data/0.6.3/) +- [structlog: 23.2.0](https://pypi.org/project/structlog/23.2.0/) +- [tabulate: 0.9.0](https://pypi.org/project/tabulate/0.9.0/) +- [tenacity: 8.2.3](https://pypi.org/project/tenacity/8.2.3/) +- [terminado: 0.18.0](https://pypi.org/project/terminado/0.18.0/) +- [threadpoolctl: 3.2.0](https://pypi.org/project/threadpoolctl/3.2.0/) +- [tinycss2: 1.2.1](https://pypi.org/project/tinycss2/1.2.1/) +- [toml: 0.10.2](https://pypi.org/project/toml/0.10.2/) +- [tomli: 2.0.1](https://pypi.org/project/tomli/2.0.1/) +- [tornado: 6.4](https://pypi.org/project/tornado/6.4/) +- [tqdm: 4.66.1](https://pypi.org/project/tqdm/4.66.1/) +- [traitlets: 5.14.0](https://pypi.org/project/traitlets/5.14.0/) +- [types-python-dateutil: 2.8.19.14](https://pypi.org/project/types-python-dateutil/2.8.19.14/) +- [typing-extensions: 4.8.0](https://pypi.org/project/typing-extensions/4.8.0/) +- [tzdata: 2023.3](https://pypi.org/project/tzdata/2023.3/) +- [upsetplot: 0.8.0](https://pypi.org/project/upsetplot/0.8.0/) +- [uri-template: 1.3.0](https://pypi.org/project/uri-template/1.3.0/) +- [urllib3: 2.1.0](https://pypi.org/project/urllib3/2.1.0/) +- [venn: 0.1.3](https://pypi.org/project/venn/0.1.3/) +- [wcwidth: 0.2.12](https://pypi.org/project/wcwidth/0.2.12/) +- [webcolors: 1.13](https://pypi.org/project/webcolors/1.13/) +- [webencodings: 0.5.1](https://pypi.org/project/webencodings/0.5.1/) +- [websocket-client: 1.7.0](https://pypi.org/project/websocket-client/1.7.0/) +- [wheel: 0.42.0](https://pypi.org/project/wheel/0.42.0/) +- [widgetsnbextension: 4.0.9](https://pypi.org/project/widgetsnbextension/4.0.9/) +- [wrapt: 1.16.0](https://pypi.org/project/wrapt/1.16.0/) + diff --git a/v2/requirements.in b/v2/requirements.in new file mode 100644 index 0000000..82a8776 --- /dev/null +++ b/v2/requirements.in @@ -0,0 +1,38 @@ +# Basic requirements for notebook infrastructure provided in base +# docker image +pip-tools +notebook +jupyter-console +jupyterlab +jupytext +bash_kernel +nbval +opensafely-matching + +# Commonly-used packages provided in base docker image +pandas +numpy +matplotlib +scipy +tqdm +numba +opensafely-cohort-extractor +pyarrow +venn +kaleido +scikit-learn +lifelines + +# Both these required for plotly+notebooks +plotly +ipywidgets + +# Add extra per-notebook packages here +pillow +cairosvg + +# Allow for projects to run tests inside the container +pytest + +# For visualisation of set overlaps +upsetplot diff --git a/v2/requirements.txt b/v2/requirements.txt new file mode 100644 index 0000000..cf29f44 --- /dev/null +++ b/v2/requirements.txt @@ -0,0 +1,499 @@ +# +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: +# +# pip-compile --output-file=v2/requirements.txt v2/requirements.in +# +anyio==4.1.0 + # via jupyter-server +argon2-cffi==23.1.0 + # via jupyter-server +argon2-cffi-bindings==21.2.0 + # via argon2-cffi +arrow==1.3.0 + # via isoduration +astor==0.8.1 + # via formulaic +asttokens==2.4.1 + # via stack-data +async-lru==2.0.4 + # via jupyterlab +attrs==23.1.0 + # via + # jsonschema + # referencing +autograd==1.6.2 + # via + # autograd-gamma + # lifelines +autograd-gamma==0.5.0 + # via lifelines +babel==2.13.1 + # via jupyterlab-server +bash-kernel==0.9.3 + # via -r v2/requirements.in +beautifulsoup4==4.12.2 + # via nbconvert +bleach==6.1.0 + # via nbconvert +build==1.0.3 + # via pip-tools +cairocffi==1.6.1 + # via cairosvg +cairosvg==2.7.1 + # via -r v2/requirements.in +certifi==2023.11.17 + # via requests +cffi==1.16.0 + # via + # argon2-cffi-bindings + # cairocffi +charset-normalizer==3.3.2 + # via requests +click==8.1.7 + # via pip-tools +comm==0.2.0 + # via + # ipykernel + # ipywidgets +contourpy==1.2.0 + # via matplotlib +coverage==7.3.2 + # via nbval +cssselect2==0.7.0 + # via cairosvg +cycler==0.12.1 + # via matplotlib +debugpy==1.8.0 + # via ipykernel +decorator==5.1.1 + # via + # ipython + # retry +defusedxml==0.7.1 + # via + # cairosvg + # nbconvert +exceptiongroup==1.2.0 + # via + # anyio + # ipython + # pytest +executing==2.0.1 + # via stack-data +fastjsonschema==2.19.0 + # via nbformat +fonttools==4.46.0 + # via matplotlib +formulaic==0.6.6 + # via lifelines +fqdn==1.5.1 + # via jsonschema +future==0.18.3 + # via autograd +idna==3.6 + # via + # anyio + # jsonschema + # requests +iniconfig==2.0.0 + # via pytest +interface-meta==1.3.0 + # via formulaic +ipykernel==6.27.1 + # via + # bash-kernel + # jupyter-console + # jupyterlab + # nbval +ipython==8.18.1 + # via + # ipykernel + # ipywidgets + # jupyter-console +ipywidgets==8.1.1 + # via -r v2/requirements.in +isoduration==20.11.0 + # via jsonschema +jedi==0.19.1 + # via ipython +jinja2==3.1.2 + # via + # jupyter-server + # jupyterlab + # jupyterlab-server + # nbconvert +joblib==1.3.2 + # via scikit-learn +json5==0.9.14 + # via jupyterlab-server +jsonpointer==2.4 + # via jsonschema +jsonschema[format-nongpl]==4.20.0 + # via + # jupyter-events + # jupyterlab-server + # nbformat +jsonschema-specifications==2023.11.2 + # via jsonschema +jupyter-client==8.6.0 + # via + # ipykernel + # jupyter-console + # jupyter-server + # nbclient + # nbval +jupyter-console==6.6.3 + # via -r v2/requirements.in +jupyter-core==5.5.0 + # via + # ipykernel + # jupyter-client + # jupyter-console + # jupyter-server + # jupyterlab + # nbclient + # nbconvert + # nbformat +jupyter-events==0.9.0 + # via jupyter-server +jupyter-lsp==2.2.1 + # via jupyterlab +jupyter-server==2.11.2 + # via + # jupyter-lsp + # jupyterlab + # jupyterlab-server + # notebook + # notebook-shim +jupyter-server-terminals==0.4.4 + # via jupyter-server +jupyterlab==4.0.9 + # via + # -r v2/requirements.in + # notebook +jupyterlab-pygments==0.3.0 + # via nbconvert +jupyterlab-server==2.25.2 + # via + # jupyterlab + # notebook +jupyterlab-widgets==3.0.9 + # via ipywidgets +jupytext==1.16.0 + # via -r v2/requirements.in +kaleido==0.2.1 + # via -r v2/requirements.in +kiwisolver==1.4.5 + # via matplotlib +lifelines==0.27.8 + # via -r v2/requirements.in +llvmlite==0.41.1 + # via numba +lz4==4.3.2 + # via opensafely-cohort-extractor +markdown-it-py==3.0.0 + # via + # jupytext + # mdit-py-plugins +markupsafe==2.1.3 + # via + # jinja2 + # nbconvert +matplotlib==3.8.2 + # via + # -r v2/requirements.in + # lifelines + # seaborn + # upsetplot + # venn +matplotlib-inline==0.1.6 + # via + # ipykernel + # ipython +mdit-py-plugins==0.4.0 + # via jupytext +mdurl==0.1.2 + # via markdown-it-py +mistune==3.0.2 + # via nbconvert +nbclient==0.9.0 + # via nbconvert +nbconvert==7.12.0 + # via jupyter-server +nbformat==5.9.2 + # via + # jupyter-server + # jupytext + # nbclient + # nbconvert + # nbval +nbval==0.10.0 + # via -r v2/requirements.in +nest-asyncio==1.5.8 + # via ipykernel +notebook==7.0.6 + # via -r v2/requirements.in +notebook-shim==0.2.3 + # via + # jupyterlab + # notebook +numba==0.58.1 + # via -r v2/requirements.in +numpy==1.26.2 + # via + # -r v2/requirements.in + # autograd + # contourpy + # formulaic + # lifelines + # matplotlib + # numba + # pandas + # pyarrow + # scikit-learn + # scipy + # seaborn +opensafely-cohort-extractor==1.90.0 + # via -r v2/requirements.in +opensafely-matching==0.2.0 + # via -r v2/requirements.in +overrides==7.4.0 + # via jupyter-server +packaging==23.2 + # via + # build + # ipykernel + # jupyter-server + # jupyterlab + # jupyterlab-server + # jupytext + # matplotlib + # nbconvert + # plotly + # pytest +pandas==2.1.3 + # via + # -r v2/requirements.in + # formulaic + # lifelines + # opensafely-cohort-extractor + # opensafely-matching + # seaborn + # upsetplot +pandocfilters==1.5.0 + # via nbconvert +parso==0.8.3 + # via jedi +pexpect==4.9.0 + # via + # bash-kernel + # ipython +pillow==10.1.0 + # via + # -r v2/requirements.in + # cairosvg + # matplotlib +pip-tools==7.3.0 + # via -r v2/requirements.in +platformdirs==4.1.0 + # via jupyter-core +plotly==5.18.0 + # via -r v2/requirements.in +pluggy==1.3.0 + # via pytest +prometheus-client==0.19.0 + # via jupyter-server +prompt-toolkit==3.0.41 + # via + # ipython + # jupyter-console +psutil==5.9.6 + # via ipykernel +ptyprocess==0.7.0 + # via + # pexpect + # terminado +pure-eval==0.2.2 + # via stack-data +py==1.11.0 + # via retry +pyarrow==14.0.1 + # via + # -r v2/requirements.in + # opensafely-cohort-extractor +pycparser==2.21 + # via cffi +pygments==2.17.2 + # via + # ipython + # jupyter-console + # nbconvert +pyparsing==3.1.1 + # via matplotlib +pyproject-hooks==1.0.0 + # via build +pytest==7.4.3 + # via + # -r v2/requirements.in + # nbval +python-dateutil==2.8.2 + # via + # arrow + # jupyter-client + # matplotlib + # pandas +python-json-logger==2.0.7 + # via jupyter-events +pytz==2023.3.post1 + # via pandas +pyyaml==6.0.1 + # via + # jupyter-events + # jupytext + # opensafely-cohort-extractor +pyzmq==25.1.2 + # via + # ipykernel + # jupyter-client + # jupyter-console + # jupyter-server +referencing==0.31.1 + # via + # jsonschema + # jsonschema-specifications + # jupyter-events +requests==2.31.0 + # via + # jupyterlab-server + # opensafely-cohort-extractor +retry==0.9.2 + # via opensafely-cohort-extractor +rfc3339-validator==0.1.4 + # via + # jsonschema + # jupyter-events +rfc3986-validator==0.1.1 + # via + # jsonschema + # jupyter-events +rpds-py==0.13.2 + # via + # jsonschema + # referencing +scikit-learn==1.3.2 + # via -r v2/requirements.in +scipy==1.11.4 + # via + # -r v2/requirements.in + # autograd-gamma + # formulaic + # lifelines + # scikit-learn +seaborn==0.13.0 + # via opensafely-cohort-extractor +send2trash==1.8.2 + # via jupyter-server +six==1.16.0 + # via + # asttokens + # bleach + # python-dateutil + # rfc3339-validator +sniffio==1.3.0 + # via anyio +soupsieve==2.5 + # via beautifulsoup4 +sqlparse==0.4.4 + # via opensafely-cohort-extractor +stack-data==0.6.3 + # via ipython +structlog==23.2.0 + # via opensafely-cohort-extractor +tabulate==0.9.0 + # via opensafely-cohort-extractor +tenacity==8.2.3 + # via plotly +terminado==0.18.0 + # via + # jupyter-server + # jupyter-server-terminals +threadpoolctl==3.2.0 + # via scikit-learn +tinycss2==1.2.1 + # via + # cairosvg + # cssselect2 + # nbconvert +toml==0.10.2 + # via jupytext +tomli==2.0.1 + # via + # build + # jupyterlab + # pip-tools + # pyproject-hooks + # pytest +tornado==6.4 + # via + # ipykernel + # jupyter-client + # jupyter-server + # jupyterlab + # notebook + # terminado +tqdm==4.66.1 + # via -r v2/requirements.in +traitlets==5.14.0 + # via + # comm + # ipykernel + # ipython + # ipywidgets + # jupyter-client + # jupyter-console + # jupyter-core + # jupyter-events + # jupyter-server + # jupyterlab + # matplotlib-inline + # nbclient + # nbconvert + # nbformat +types-python-dateutil==2.8.19.14 + # via arrow +typing-extensions==4.8.0 + # via + # async-lru + # formulaic +tzdata==2023.3 + # via pandas +upsetplot==0.8.0 + # via -r v2/requirements.in +uri-template==1.3.0 + # via jsonschema +urllib3==2.1.0 + # via requests +venn==0.1.3 + # via -r v2/requirements.in +wcwidth==0.2.12 + # via prompt-toolkit +webcolors==1.13 + # via jsonschema +webencodings==0.5.1 + # via + # bleach + # cssselect2 + # tinycss2 +websocket-client==1.7.0 + # via jupyter-server +wheel==0.42.0 + # via pip-tools +widgetsnbextension==4.0.9 + # via ipywidgets +wrapt==1.16.0 + # via formulaic + +# The following packages are considered to be unsafe in a requirements file: +# pip +# setuptools