From aec0c15196fd5029510da9a7c094e2739b4af60c Mon Sep 17 00:00:00 2001 From: Diego Quintana Date: Sat, 2 Sep 2023 15:31:58 +0200 Subject: [PATCH] blog: add python with docker blog entry (#10) * dev: update dependencies * blog: add docker with python entry --- content/blog/14_devcontainers_and_python.md | 561 ++++++++++++++++---- poetry.lock | 347 +++++++++++- pyproject.toml | 3 + 3 files changed, 793 insertions(+), 118 deletions(-) diff --git a/content/blog/14_devcontainers_and_python.md b/content/blog/14_devcontainers_and_python.md index d2e5040..fc0d4ad 100644 --- a/content/blog/14_devcontainers_and_python.md +++ b/content/blog/14_devcontainers_and_python.md @@ -1,185 +1,520 @@ Title: Python, Docker and devcontainers -Date: 2023-07-03 02:00:00 +0200 +Date: 2023-09-02 15:00:00 +0200 Category: Development Tags: python, containers -Slug: python-devcontainers +Slug: python-docker Authors: Diego Quintana -Status: draft -Summary: Getting devcontainers right with python and multistage docker builds +Status: published +Summary: Getting python containers right -## Example multistage docker image +[TOC] +## What -I've toyed with this fair amount of time. There's a lot going on so let me wrap it up for you: +I've toyed with docker a fair amount of time. There's a lot going on to get a working container so let me summarize a bit of what I'll show here: -1. We use a multistage docker build to separate the build stage from the app stage -2. We use `pipx` to install `poetry` and `poetry` to install our dependencies -3. We install dependencies at a app-pre stage and copy them to the app stage -4. We pivot from the `app-pre` stage for launching devcontainers and from the `app` stage for production -5. We create a user and a group to avoid running as root at both levels. At both dev and prod levels, this user may or not may have access to sudo. +1. We use a multistage docker build to separate the **build** (`builder`) stage from the **final** (`app`) stage and a development stage (`dev`) for development purposes. +2. We use `pipx` to install `poetry` and `poetry` to install our dependencies. +3. We pivot from the `app-pre` stage for doing development e.g. using _devcontainers_, and from the `app` stage for production. +4. We create a user and a group to avoid running as root at both levels. At both dev and prod levels, this user may or not may have access to `sudo`. +What follows are my dry, unpolished notes about how to do this. My goal is to turn this into a workshop at some point. +## Docker image requirements -```docker -## ---------------------------------------------------------------------------- # -## example usage for docker and poetry # -## ---------------------------------------------------------------------------- # +When building a docker image, we wish to achieve the following: -## ---------------------------------------------------------------------------- # -## build stage # -## ---------------------------------------------------------------------------- # +1. Security: we don't want to run as root in production. In development, we may or may not want to run as root. We achieve this by defining a user and a group and granting sudo access depending on the docker build stage. There are rootless options such as _podman_ or even docker itself, but I haven't explored them yet. +2. Size: we want to keep the image size as small as possible. We achieve this by using a multistage build where the final image is based on a slim python image and it only contains the virtual environment with the dependencies, installed using `poetry` at a previous stage. +3. Speed: we want to keep the build time as small as possible. We can achieve this by leveraging docker caching and by separating the build stage from the final stage. +4. Flexibility: we want to be able to use the same image for development and production. We achieve this by defining a `dev` stage that is based on the `app-pre` stage and that allows us to install additional dependencies for development purposes. +5. Stability: we want to pin versions for stable builds. We achieve this by defining the versions of `poetry` and `pipx` that we want to use. We use a pyproject.toml file to define the versions of our dependencies. -## Global ARG, available to all stages (if renewed) +I'm not going into docker tagging and versioning here, which is much more involved and I haven't figured out yet how to do it elegantly. + +## Example project + +We'll use a simple python project with a `pyproject.toml` file and a `poetry.lock` file. We'll use `poetry` to install our dependencies. + +```bash +mkdir -p myproject +cd myproject +touch pyproject.toml poetry.lock +``` + +Edit the `pyproject.toml` file to add some dependencies: + +```toml +[tool.poetry] +authors = ["Your Name "] +description = "An example project to showcase docker and poetry" +name = "myproject" +version = "0.1.0" + +[tool.poetry.dependencies] +python = ">=3.8,<3.11" +cowsay = "^4.0.0" + +[build-system] +build-backend = "poetry.core.masonry.api" +requires = ["poetry-core"] +``` + +## Global variables + +We start by defining a global group of variables that will be used across many stages. + +```Dockerfile +# ---------------------------------------------------------------------------- # +# global build arguments # +# ---------------------------------------------------------------------------- # + +# Global ARG, available to all stages (if renewed) ARG WORKDIR="/app" -## tag used in all images -ARG PYTHON_VERSION=3.9.16 +# global username +ARG USERNAME=bluesmonk +ARG USER_UID=1000 +ARG USER_GID=1000 + +# tag used in all images +ARG PYTHON_VERSION=3.8.9 +``` + +We can reference these variables later at every stage by simply using the same `ARG` statement. For example, we can use `ARG WORKDIR` at every stage to reference the same value. + +## The `builder` stage -FROM python:${PYTHON_VERSION} AS builder +Give it a read first and then we'll go through it step by step. -## Renew (https://stackoverflow.com/a/53682110): +```Dockerfile + +# ---------------------------------------------------------------------------- # +# build stage # +# ---------------------------------------------------------------------------- # + +FROM python:${PYTHON_VERSION}-slim AS builder + +# Renew args ARG WORKDIR +ARG USERNAME +ARG USER_UID +ARG USER_GID -## Poetry version -ARG POETRY_VERSION=1.4.0 +# Poetry version +ARG POETRY_VERSION=1.5.1 -## Pipx version +# Pipx version ARG PIPX_VERSION=1.2.0 -## prepare the $PATH +# prepare the $PATH ENV PATH=/opt/pipx/bin:${WORKDIR}/.venv/bin:$PATH \ - PIPX_BIN_DIR=/opt/pipx/bin \ - PIPX_HOME=/opt/pipx/home \ - PIPX_VERSION=$PIPX_VERSION \ - POETRY_VERSION=$POETRY_VERSION \ - PYTHONPATH=${WORKDIR} \ - ## Don't buffer `stdout` - PYTHONUNBUFFERED=1 \ - ## Don't create `.pyc` files: - PYTHONDONTWRITEBYTECODE=1 \ - ## make poetry create a .venv folder in the project - POETRY_VIRTUALENVS_IN_PROJECT=true - -## Install Pipx using pip + PIPX_BIN_DIR=/opt/pipx/bin \ + PIPX_HOME=/opt/pipx/home \ + PIPX_VERSION=$PIPX_VERSION \ + POETRY_VERSION=$POETRY_VERSION \ + PYTHONPATH=${WORKDIR} \ + # Don't buffer `stdout` + PYTHONUNBUFFERED=1 \ + # Don't create `.pyc` files: + PYTHONDONTWRITEBYTECODE=1 \ + # make poetry create a .venv folder in the project + POETRY_VIRTUALENVS_IN_PROJECT=true + +# ------------------------------ add user ----------------------------- # + +RUN groupadd --gid $USER_GID "${USERNAME}" \ + && useradd --uid $USER_UID --gid $USER_GID -m "${USERNAME}" + +# -------------------------- add python dependencies ------------------------- # + +# Install Pipx using pip RUN python -m pip install --no-cache-dir --upgrade pip pipx==${PIPX_VERSION} RUN pipx ensurepath && pipx --version -## Install Poetry using pipx +# Install Poetry using pipx RUN pipx install --force poetry==${POETRY_VERSION} -## Copy everything to the container, we filter out what we don't need using .dockerignore +# ---------------------------- add code specifics ---------------------------- # + +# Copy everything to the container +# we filter out what we don't need using .dockerignore WORKDIR ${WORKDIR} -COPY pyproject.toml poetry.lock ./ -## ---------------------------------------------------------------------------- # -## app pre-stage # -## ---------------------------------------------------------------------------- # +# make sure the user owns /app +RUN chown -R ${USER_UID}:${USER_GID} ${WORKDIR} + +# Copy only the files needed for installing dependencies +COPY --chown=${USER_UID}:${USER_GID} pyproject.toml poetry.lock ${WORKDIR}/ + +USER ${USERNAME} +``` + +1. We start by defining the `builder` stage. We use a slim python image as a base image. We use the `AS` keyword to name the stage. +2. We renew the global variables that we defined earlier. We do this because we want to use the same variables at every stage. We can do this by simply using the same `ARG` statement. +3. We define some additional variables that we'll use later. We define the versions of `poetry` and `pipx` that we want to use. We do this because we want to pin versions for stable builds. +4. We define the `$PATH` variable to control and configure python, poetry and pipx installations beforehand + 1. We configure `pipx` installation so that installer uses custom directories. + 2. We also add the virtual environment directory to the path so that we can run the project from anywhere. + 3. We define the `PYTHONPATH` variable to point to the project directory at `/app` so that we can import modules from the project. + 4. We configure poetry using the `POETRY_VIRTUALENVS_IN_PROJECT` variable to make `poetry` create a `.venv` folder in the project, at `/app/.venv`. +5. We add a user and a group to avoid running as root. We don't use this user now, but we will later. +6. We install `pipx` using `pip` and later we install `poetry` using `pipx`. +7. We define our working directory at `/app`. This directory needs to be writable by the user that we created earlier. We also copy the `pyproject.toml` and `poetry.lock` files to the container. +8. We seal the stage by switching to the user that we created earlier. From this point on, all commands will be run as this user unless we switch to another user. + +## The `app-pre` stage + +This stage starts from the `builder` stage and it installs only main dependencies using `poetry`. We pass the `--no-root` flag to `poetry` to avoid installing the project itself. We use this stage to install dependencies and to create a virtual environment at `/app/.venv`. + +```Dockerfile + +# ---------------------------------------------------------------------------- # +# app-pre stage # +# ---------------------------------------------------------------------------- # + +FROM builder AS app-pre + +# Install dependencies and creates a a virtualenv at /app/.venv +RUN poetry install --no-root --only main +``` + +## The `app` stage -FROM builder as app-pre +This is mostly the same as the builder stage, but with a few changes: -## Install dependencies -RUN poetry install --with dev +1. We use a slim python image as a base image. We use the `AS` keyword to name the stage. At this point you can use any image you want, provided it has python installed. We make the image tag depend on the python version that we defined earlier. +2. We copy the directory at `/app/.venv` from the `app-pre` stage to the `app` stage. We do this because we want to keep the virtual environment that we created at the `app-pre` stage, but we don't need poetry and pipx anymore. +3. We don't need to install `poetry` and `pipx` anymore, so we remove them from the `$PATH` variable. -## ---------------------------------------------------------------------------- # -## app stage # -## ---------------------------------------------------------------------------- # +```Dockerfile -## We don't want to use alpine -## https://stackoverflow.com/a/67695490/5819113 -FROM python:${PYTHON_VERSION}-slim as app +# ---------------------------------------------------------------------------- # +# app stage # +# ---------------------------------------------------------------------------- # -## refresh ARG +# We don't want to use alpine because porting from debian is challenging +# https://stackoverflow.com/a/67695490/5819113 +FROM python:${PYTHON_VERSION}-slim AS app + +# refresh global arguments ARG WORKDIR +ARG USERNAME +ARG USER_UID +ARG USER_GID -## refresh PATH +# refresh PATH ENV PATH=/opt/pipx/bin:${WORKDIR}/.venv/bin:$PATH \ - POETRY_VERSION=$POETRY_VERSION \ - PYTHONPATH=${WORKDIR} \ - ## Don't buffer `stdout` - PYTHONUNBUFFERED=1 \ - ## Don't create `.pyc` files: - PYTHONDONTWRITEBYTECODE=1 + POETRY_VERSION=$POETRY_VERSION \ + PYTHONPATH=${WORKDIR} \ + # Don't buffer `stdout` + PYTHONUNBUFFERED=1 \ + # Don't create `.pyc` files: + PYTHONDONTWRITEBYTECODE=1 + +# ------------------------------ user management ----------------------------- # + +RUN groupadd --gid $USER_GID "${USERNAME}" \ + && useradd --uid $USER_UID --gid $USER_GID -m "${USERNAME}" + +# ------------------------------- app specific ------------------------------- # WORKDIR ${WORKDIR} -COPY --from=app-pre ${WORKDIR} . +RUN chown -R ${USER_UID}:${USER_GID} ${WORKDIR} -## ---------------------------------------------------------------------------- # -## user management # -## ---------------------------------------------------------------------------- # +COPY --from=app-pre --chown=${USER_UID}:${USER_GID} ${WORKDIR} ${WORKDIR} -## For more on users and groups -## see https://www.debian.org/doc/debian-policy/ch-opersys.html#uid-and-gid-classes -## see https://stackoverflow.com/a/55757473/5819113 -## see https://stackoverflow.com/questions/56844746/how-to-set-uid-and-gid-in-docker-compose -## see https://nickjanetakis.com/blog/running-docker-containers-as-a-non-root-user-with-a-custom-uid-and-gid +USER ${USERNAME} -ARG UID -ARG GID -ARG UNAME +ENTRYPOINT [ "python" ] +CMD [ "--version" ] +``` -ENV UID=${UID:-1000} -ENV GID=${GID:-1000} -ENV UNAME=${UNAME:-somenergia} +## The `dev` stage -RUN groupadd \ - --gid $GID \ - "$UNAME" && \ - useradd \ - --no-log-init \ - --home-dir "${WORKDIR}" \ - --uid $UID \ - --gid $GID \ - --no-create-home \ - "$UNAME" +This stage starts from the `app-pre` stage and it installs all remaining dependencies using `poetry`. It looks like the `app` stage, but with a few changes: -USER ${UID} +1. We install development dependencies such as `git`, `sudo`, `wget` and `iputils-ping`. We do this because we may want to be able to install additional software after connecting to the container. +2. We install extra python dependencies for development purposes. We do this by adding a `dev` section to the `pyproject.toml` file and by passing the `--no-root` flag to `poetry` to avoid installing the project itself. +3. We add the user to the `sudoers` file. This is not recommended for production. +4. We seal the stage by switching to the user that we created earlier. This user has `sudo` access. -## ---------------------------------------------------------------------------- # -## App-specific settings # -## ---------------------------------------------------------------------------- # +```Dockerfile -ENTRYPOINT [ "python" ] -CMD [ "--version" ] +# ---------------------------------------------------------------------------- # +# dev stage # +# ---------------------------------------------------------------------------- # + +FROM app-pre AS dev + +# refresh global arguments +ARG WORKDIR +ARG USERNAME +ARG USER_UID +ARG USER_GID + + +USER root + +# Add USERNAME to sudoers. Omit if you don't need to install software after connecting. +RUN apt-get update \ + && apt-get install -y sudo git iputils-ping wget \ + && echo ${USERNAME} ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/${USERNAME} \ + && chmod 0440 /etc/sudoers.d/${USERNAME} + +USER ${USERNAME} + +# install all remaning dependencies +RUN poetry install --no-root --with dev + +USER ${USERNAME} ``` -## Docker compose file +## Buiding using docker compose +Edit a docker compose file to build the image at `docker-compose.yml` ```yaml --- -version: "3" +name: docker-poetry-project + +version: "3.7" services: + builder: + image: "myproject-builder:latest" + deploy: + replicas: 0 # never start service, since it is only for build purposes + build: + context: . + dockerfile: Dockerfile + target: builder + cache_from: + - "myproject-builder:latest" app: - image: "${DOCKER_IMAGE?Variable not set}:${DOCKER_TAG-latest}" + image: "myproject-app:latest" + deploy: + replicas: 0 # never start service, since it is only for build purposes build: context: . dockerfile: Dockerfile target: app - args: - - "UID=${UID:-1000}" - - "GID=${GID:-1000}" - - "UNAME=${UNAME:-myname}" - - builder: - image: "${DOCKER_IMAGE?Variable not set}:${DOCKER_TAG-latest}-builder" + cache_from: + - "myproject-builder:latest" + - "myproject-app:latest" + app-dev: + image: "myproject-app-dev:latest" build: context: . dockerfile: Dockerfile - target: builder - args: - - "UID=${UID:-1000}" - - "GID=${GID:-1000}" - - "UNAME=${UNAME:-somenergia}" + target: dev + cache_from: + - "myproject-builder:latest" + - "myproject-app-dev:latest" + volumes: + - python_venv_app_dev:/app/.venv # mount virtualenv at /venv. See https://stackoverflow.com/a/74015989/5819113 + command: /bin/sh -c "sleep infinity" # keep container running + +volumes: + python_venv_app_dev: ``` -```yaml +A few notes about this file: + +1. Using the `cache_from` option, we can use the cache from previous builds. This is useful to speed up the build process. +2. Setting `replicas: 0` ensures that the service is never started. We only use it for building the image. +3. We mount the virtual environment at `/app/.venv` using a _named volume_. This is very important because we don't want to share this directory with the host machine. This is useful for development purposes because otherwise you may run into permission issues when trying to install dependencies from the host machine (e.g. if you decide to use another user id or another group). See for details. + +Using this file, we can build the images using `docker compose build`. + +## Try out your new containers + +Use docker compose to get a shell inside the `app-dev` container. + +```bash docker compose build ``` -## Devcontainer +```bash +docker compose up -d app-dev +``` + +```bash +docker compose exec -it --entrypoint bash app-dev +``` + +```bash +python --version +python -c "import cowsay; cowsay.cow('Hello world')" +ls -la +exit +``` + +To get inside the `app` container, use `docker compose run --rm -it --entrypoint bash app`. + +## Full Dockerfile + +```Dockerfile +# ---------------------------------------------------------------------------- # +# example usage for docker and poetry # +# ---------------------------------------------------------------------------- # + + +# ---------------------------------------------------------------------------- # +# global build arguments # +# ---------------------------------------------------------------------------- # + +# Global ARG, available to all stages (if renewed) +ARG WORKDIR="/app" + +# global username +ARG USERNAME=bluesmonk +ARG USER_UID=1000 +ARG USER_GID=1000 + +# tag used in all images +ARG PYTHON_VERSION=3.8.9 + +# ---------------------------------------------------------------------------- # +# build stage # +# ---------------------------------------------------------------------------- # + +FROM python:${PYTHON_VERSION}-slim AS builder + +# Renew args +ARG WORKDIR +ARG USERNAME +ARG USER_UID +ARG USER_GID + +# Poetry version +ARG POETRY_VERSION=1.5.1 + +# Pipx version +ARG PIPX_VERSION=1.2.0 + +# prepare the $PATH +ENV PATH=/opt/pipx/bin:${WORKDIR}/.venv/bin:$PATH \ + PIPX_BIN_DIR=/opt/pipx/bin \ + PIPX_HOME=/opt/pipx/home \ + PIPX_VERSION=$PIPX_VERSION \ + POETRY_VERSION=$POETRY_VERSION \ + PYTHONPATH=${WORKDIR} \ + # Don't buffer `stdout` + PYTHONUNBUFFERED=1 \ + # Don't create `.pyc` files: + PYTHONDONTWRITEBYTECODE=1 \ + # make poetry create a .venv folder in the project + POETRY_VIRTUALENVS_IN_PROJECT=true + +# ------------------------------ add user ----------------------------- # + +RUN groupadd --gid $USER_GID "${USERNAME}" \ + && useradd --uid $USER_UID --gid $USER_GID -m "${USERNAME}" + +# -------------------------- add python dependencies ------------------------- # + +# Install Pipx using pip +RUN python -m pip install --no-cache-dir --upgrade pip pipx==${PIPX_VERSION} +RUN pipx ensurepath && pipx --version + +# Install Poetry using pipx +RUN pipx install --force poetry==${POETRY_VERSION} + +# ---------------------------- add code specifics ---------------------------- # + +# Copy everything to the container +# we filter out what we don't need using .dockerignore +WORKDIR ${WORKDIR} + +# make sure the user owns /app +RUN chown -R ${USER_UID}:${USER_GID} ${WORKDIR} + +# Copy only the files needed for installing dependencies +COPY --chown=${USER_UID}:${USER_GID} pyproject.toml poetry.lock ${WORKDIR}/ + +USER ${USERNAME} - + +# ---------------------------------------------------------------------------- # +# app-pre stage # +# ---------------------------------------------------------------------------- # + +FROM builder AS app-pre + +# Install dependencies and creates a a virtualenv at /app/.venv +RUN poetry install --no-root --only main + +# ---------------------------------------------------------------------------- # +# app stage # +# ---------------------------------------------------------------------------- # + +# We don't want to use alpine because porting from debian is challenging +# https://stackoverflow.com/a/67695490/5819113 +FROM python:${PYTHON_VERSION}-slim AS app + +# refresh global arguments +ARG WORKDIR +ARG USERNAME +ARG USER_UID +ARG USER_GID + +# refresh PATH +ENV PATH=/opt/pipx/bin:${WORKDIR}/.venv/bin:$PATH \ + POETRY_VERSION=$POETRY_VERSION \ + PYTHONPATH=${WORKDIR} \ + # Don't buffer `stdout` + PYTHONUNBUFFERED=1 \ + # Don't create `.pyc` files: + PYTHONDONTWRITEBYTECODE=1 + +# ------------------------------ user management ----------------------------- # + +RUN groupadd --gid $USER_GID "${USERNAME}" \ + && useradd --uid $USER_UID --gid $USER_GID -m "${USERNAME}" + +# ------------------------------- app specific ------------------------------- # + +WORKDIR ${WORKDIR} + +RUN chown -R ${USER_UID}:${USER_GID} ${WORKDIR} + +COPY --from=app-pre --chown=${USER_UID}:${USER_GID} ${WORKDIR} ${WORKDIR} + +USER ${USERNAME} + +ENTRYPOINT [ "python" ] +CMD [ "--version" ] + + +# ---------------------------------------------------------------------------- # +# dev stage # +# ---------------------------------------------------------------------------- # + +FROM app-pre AS dev + +# refresh global arguments +ARG WORKDIR +ARG USERNAME +ARG USER_UID +ARG USER_GID + + +USER root + +# Add USERNAME to sudoers. Omit if you don't need to install software after connecting. +RUN apt-get update \ + && apt-get install -y sudo git iputils-ping wget \ + && echo ${USERNAME} ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/${USERNAME} \ + && chmod 0440 /etc/sudoers.d/${USERNAME} + +USER ${USERNAME} + +# install all remaning dependencies +RUN poetry install --no-root + +USER ${USERNAME} +``` ## References @@ -198,6 +533,6 @@ docker compose build - - -### About renewing Arguments at multiple stages +### About renewing arguments at multiple stages - diff --git a/poetry.lock b/poetry.lock index 70668ae..3bd5496 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,9 +1,10 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry and should not be changed by hand. [[package]] name = "anyio" version = "3.7.1" description = "High level compatibility layer for multiple asynchronous event loop implementations" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -25,6 +26,7 @@ trio = ["trio (<0.22)"] name = "appnope" version = "0.1.3" description = "Disable App Nap on macOS >= 10.9" +category = "dev" optional = false python-versions = "*" files = [ @@ -36,6 +38,7 @@ files = [ name = "argon2-cffi" version = "21.3.0" description = "The secure Argon2 password hashing algorithm." +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -55,6 +58,7 @@ tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"] name = "argon2-cffi-bindings" version = "21.2.0" description = "Low-level CFFI bindings for Argon2" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -92,6 +96,7 @@ tests = ["pytest"] name = "asttokens" version = "2.2.1" description = "Annotate AST trees with source code positions" +category = "dev" optional = false python-versions = "*" files = [ @@ -109,6 +114,7 @@ test = ["astroid", "pytest"] name = "attrs" version = "23.1.0" description = "Classes Without Boilerplate" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -127,6 +133,7 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte name = "backcall" version = "0.2.0" description = "Specifications for callback functions passed in to an API" +category = "dev" optional = false python-versions = "*" files = [ @@ -138,6 +145,7 @@ files = [ name = "beautifulsoup4" version = "4.12.2" description = "Screen-scraping library" +category = "main" optional = false python-versions = ">=3.6.0" files = [ @@ -156,6 +164,7 @@ lxml = ["lxml"] name = "black" version = "22.12.0" description = "The uncompromising code formatter." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -191,6 +200,7 @@ uvloop = ["uvloop (>=0.15.2)"] name = "bleach" version = "6.0.0" description = "An easy safelist-based HTML-sanitizing tool." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -209,6 +219,7 @@ css = ["tinycss2 (>=1.1.0,<1.2)"] name = "blinker" version = "1.6.2" description = "Fast, simple object-to-object and broadcast signaling" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -220,6 +231,7 @@ files = [ name = "cffi" version = "1.15.1" description = "Foreign Function Interface for Python calling C code." +category = "dev" optional = false python-versions = "*" files = [ @@ -292,10 +304,23 @@ files = [ [package.dependencies] pycparser = "*" +[[package]] +name = "cfgv" +version = "3.4.0" +description = "Validate configuration and produce human readable error messages." +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, +] + [[package]] name = "click" version = "8.1.5" description = "Composable command line interface toolkit" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -310,6 +335,7 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." +category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -321,6 +347,7 @@ files = [ name = "comm" version = "0.1.3" description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -340,6 +367,7 @@ typing = ["mypy (>=0.990)"] name = "contourpy" version = "1.1.0" description = "Python library for calculating contours of 2D quadrilateral grids" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -398,6 +426,7 @@ test-no-images = ["pytest", "pytest-cov", "wurlitzer"] name = "cycler" version = "0.11.0" description = "Composable style cycles" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -409,6 +438,7 @@ files = [ name = "debugpy" version = "1.6.7" description = "An implementation of the Debug Adapter Protocol for Python" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -436,6 +466,7 @@ files = [ name = "decorator" version = "5.1.1" description = "Decorators for Humans" +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -447,6 +478,7 @@ files = [ name = "defusedxml" version = "0.7.1" description = "XML bomb protection for Python stdlib modules" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -454,10 +486,23 @@ files = [ {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, ] +[[package]] +name = "distlib" +version = "0.3.7" +description = "Distribution utilities" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, + {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, +] + [[package]] name = "docutils" version = "0.20.1" description = "Docutils -- Python Documentation Utilities" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -469,6 +514,7 @@ files = [ name = "entrypoints" version = "0.4" description = "Discover and load entry points from installed packages." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -480,6 +526,7 @@ files = [ name = "exceptiongroup" version = "1.1.2" description = "Backport of PEP 654 (exception groups)" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -494,6 +541,7 @@ test = ["pytest (>=6)"] name = "executing" version = "1.2.0" description = "Get the currently executing AST node of a frame, and other information" +category = "dev" optional = false python-versions = "*" files = [ @@ -508,6 +556,7 @@ tests = ["asttokens", "littleutils", "pytest", "rich"] name = "fastjsonschema" version = "2.17.1" description = "Fastest Python implementation of JSON schema" +category = "main" optional = false python-versions = "*" files = [ @@ -522,6 +571,7 @@ devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benc name = "feedgenerator" version = "2.1.0" description = "Standalone version of django.utils.feedgenerator" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -532,10 +582,27 @@ files = [ [package.dependencies] pytz = ">=0a" +[[package]] +name = "filelock" +version = "3.12.2" +description = "A platform independent file lock." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"}, + {file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"}, +] + +[package.extras] +docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] + [[package]] name = "flake8" version = "6.0.0" description = "the modular source code checker: pep8 pyflakes and co" +category = "dev" optional = false python-versions = ">=3.8.1" files = [ @@ -552,6 +619,7 @@ pyflakes = ">=3.0.0,<3.1.0" name = "fonttools" version = "4.41.0" description = "Tools to manipulate font files" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -605,10 +673,26 @@ ufo = ["fs (>=2.2.0,<3)"] unicode = ["unicodedata2 (>=15.0.0)"] woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] +[[package]] +name = "identify" +version = "2.5.26" +description = "File identification library for Python" +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "identify-2.5.26-py2.py3-none-any.whl", hash = "sha256:c22a8ead0d4ca11f1edd6c9418c3220669b3b7533ada0a0ffa6cc0ef85cf9b54"}, + {file = "identify-2.5.26.tar.gz", hash = "sha256:7243800bce2f58404ed41b7c002e53d4d22bcf3ae1b7900c2d7aefd95394bf7f"}, +] + +[package.extras] +license = ["ukkonen"] + [[package]] name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -620,6 +704,7 @@ files = [ name = "importlib-metadata" version = "6.8.0" description = "Read metadata from Python packages" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -639,6 +724,7 @@ testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs name = "importlib-resources" version = "6.0.0" description = "Read resources from Python packages" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -657,6 +743,7 @@ testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", name = "ipykernel" version = "6.24.0" description = "IPython Kernel for Jupyter" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -670,7 +757,7 @@ comm = ">=0.1.1" debugpy = ">=1.6.5" ipython = ">=7.23.1" jupyter-client = ">=6.1.12" -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +jupyter-core = ">=4.12,<5.0.0 || >=5.1.0" matplotlib-inline = ">=0.1" nest-asyncio = "*" packaging = "*" @@ -690,6 +777,7 @@ test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio" name = "ipython" version = "8.12.2" description = "IPython: Productive Interactive Computing" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -729,6 +817,7 @@ test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pa name = "ipython-genutils" version = "0.2.0" description = "Vestigial utilities from IPython" +category = "main" optional = false python-versions = "*" files = [ @@ -740,6 +829,7 @@ files = [ name = "ipywidgets" version = "8.0.7" description = "Jupyter interactive widgets" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -757,10 +847,29 @@ widgetsnbextension = ">=4.0.7,<4.1.0" [package.extras] test = ["ipykernel", "jsonschema", "pytest (>=3.6.0)", "pytest-cov", "pytz"] +[[package]] +name = "isort" +version = "5.12.0" +description = "A Python utility / library to sort Python imports." +category = "dev" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, + {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, +] + +[package.extras] +colors = ["colorama (>=0.4.3)"] +pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] +plugins = ["setuptools"] +requirements-deprecated-finder = ["pip-api", "pipreqs"] + [[package]] name = "jedi" version = "0.18.2" description = "An autocompletion tool for Python that can be used for text editors." +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -780,6 +889,7 @@ testing = ["Django (<3.1)", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] name = "jinja2" version = "3.0.0" description = "A very fast and expressive template engine." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -797,6 +907,7 @@ i18n = ["Babel (>=2.7)"] name = "jsonschema" version = "4.18.3" description = "An implementation of JSON Schema validation for Python" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -820,6 +931,7 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- name = "jsonschema-specifications" version = "2023.6.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -835,6 +947,7 @@ referencing = ">=0.28.0" name = "jupyter" version = "1.0.0" description = "Jupyter metapackage. Install all the Jupyter components in one go." +category = "dev" optional = false python-versions = "*" files = [ @@ -855,6 +968,7 @@ qtconsole = "*" name = "jupyter-client" version = "8.3.0" description = "Jupyter protocol implementation and client libraries" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -864,7 +978,7 @@ files = [ [package.dependencies] importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +jupyter-core = ">=4.12,<5.0.0 || >=5.1.0" python-dateutil = ">=2.8.2" pyzmq = ">=23.0" tornado = ">=6.2" @@ -878,6 +992,7 @@ test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pyt name = "jupyter-console" version = "6.6.3" description = "Jupyter terminal console" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -889,7 +1004,7 @@ files = [ ipykernel = ">=6.14" ipython = "*" jupyter-client = ">=7.0.0" -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +jupyter-core = ">=4.12,<5.0.0 || >=5.1.0" prompt-toolkit = ">=3.0.30" pygments = "*" pyzmq = ">=17" @@ -902,6 +1017,7 @@ test = ["flaky", "pexpect", "pytest"] name = "jupyter-core" version = "5.3.1" description = "Jupyter core package. A base package on which Jupyter projects rely." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -922,6 +1038,7 @@ test = ["ipykernel", "pre-commit", "pytest", "pytest-cov", "pytest-timeout"] name = "jupyter-server" version = "1.15.6" description = "The backend—i.e. core services, APIs, and REST endpoints—to Jupyter web applications." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -954,6 +1071,7 @@ test = ["coverage", "ipykernel", "pytest (>=6.0)", "pytest-console-scripts", "py name = "jupyterlab-widgets" version = "3.0.8" description = "Jupyter interactive widgets for JupyterLab" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -965,6 +1083,7 @@ files = [ name = "kiwisolver" version = "1.4.4" description = "A fast implementation of the Cassowary constraint solver" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1042,6 +1161,7 @@ files = [ name = "markdown" version = "3.4.3" description = "Python implementation of John Gruber's Markdown." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1059,6 +1179,7 @@ testing = ["coverage", "pyyaml"] name = "markdown-it-py" version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1083,6 +1204,7 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] name = "markupsafe" version = "2.1.3" description = "Safely add untrusted strings to HTML/XML markup." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1142,6 +1264,7 @@ files = [ name = "matplotlib" version = "3.7.2" description = "Python plotting package" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1204,6 +1327,7 @@ python-dateutil = ">=2.7" name = "matplotlib-inline" version = "0.1.6" description = "Inline Matplotlib backend for Jupyter" +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -1218,6 +1342,7 @@ traitlets = "*" name = "mccabe" version = "0.7.0" description = "McCabe checker, plugin for flake8" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1229,6 +1354,7 @@ files = [ name = "mdurl" version = "0.1.2" description = "Markdown URL utilities" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1240,6 +1366,7 @@ files = [ name = "mistune" version = "0.8.4" description = "The fastest markdown parser in pure Python" +category = "main" optional = false python-versions = "*" files = [ @@ -1251,6 +1378,7 @@ files = [ name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -1262,6 +1390,7 @@ files = [ name = "nbclassic" version = "1.0.0" description = "Jupyter Notebook as a Jupyter Server extension." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1297,6 +1426,7 @@ test = ["coverage", "nbval", "pytest", "pytest-cov", "pytest-jupyter", "pytest-p name = "nbconvert" version = "5.6.1" description = "Converting Jupyter Notebooks" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -1328,6 +1458,7 @@ test = ["ipykernel", "ipywidgets (>=7)", "jupyter-client (>=5.3.1)", "mock", "pe name = "nbformat" version = "5.9.1" description = "The Jupyter Notebook format" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1349,6 +1480,7 @@ test = ["pep440", "pre-commit", "pytest", "testpath"] name = "nest-asyncio" version = "1.5.6" description = "Patch asyncio to allow nested event loops" +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -1356,10 +1488,26 @@ files = [ {file = "nest_asyncio-1.5.6.tar.gz", hash = "sha256:d267cc1ff794403f7df692964d1d2a3fa9418ffea2a3f6859a439ff482fef290"}, ] +[[package]] +name = "nodeenv" +version = "1.8.0" +description = "Node.js virtual environment builder" +category = "dev" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, +] + +[package.dependencies] +setuptools = "*" + [[package]] name = "notebook" version = "6.5.4" description = "A web-based notebook environment for interactive computing" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1394,6 +1542,7 @@ test = ["coverage", "nbval", "pytest", "pytest-cov", "requests", "requests-unixs name = "notebook-shim" version = "0.2.3" description = "A shim layer for notebook traits and config" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1411,6 +1560,7 @@ test = ["pytest", "pytest-console-scripts", "pytest-jupyter", "pytest-tornasync" name = "numpy" version = "1.24.4" description = "Fundamental package for array computing in Python" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1448,6 +1598,7 @@ files = [ name = "packaging" version = "23.1" description = "Core utilities for Python packages" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1459,6 +1610,7 @@ files = [ name = "pandocfilters" version = "1.5.0" description = "Utilities for writing pandoc filters in python" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1470,6 +1622,7 @@ files = [ name = "parso" version = "0.8.3" description = "A Python Parser" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1485,6 +1638,7 @@ testing = ["docopt", "pytest (<6.0.0)"] name = "pathspec" version = "0.11.1" description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1496,6 +1650,7 @@ files = [ name = "pelican" version = "4.8.0" description = "Static site generator supporting Markdown and reStructuredText" +category = "main" optional = false python-versions = ">=3.6,<4.0" files = [ @@ -1521,6 +1676,7 @@ markdown = ["markdown (>=3.1)"] name = "pelican-jupyter" version = "0.10.1" description = "Pelican plugin for blogging with Jupyter/IPython Notebooks" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1540,6 +1696,7 @@ test = ["pytest", "pytest-cov", "toml"] name = "pelican-render-math" version = "1.0.3" description = "Render mathematics in Pelican site content" +category = "main" optional = false python-versions = ">=3.6,<4.0" files = [ @@ -1558,6 +1715,7 @@ markdown = ["markdown (>=3.2.2,<4.0.0)"] name = "pexpect" version = "4.8.0" description = "Pexpect allows easy control of interactive console applications." +category = "dev" optional = false python-versions = "*" files = [ @@ -1572,6 +1730,7 @@ ptyprocess = ">=0.5" name = "pickleshare" version = "0.7.5" description = "Tiny 'shelve'-like database with concurrency support" +category = "dev" optional = false python-versions = "*" files = [ @@ -1583,6 +1742,7 @@ files = [ name = "pillow" version = "10.0.0" description = "Python Imaging Library (Fork)" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1604,6 +1764,7 @@ files = [ {file = "Pillow-10.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3b08d4cc24f471b2c8ca24ec060abf4bebc6b144cb89cba638c720546b1cf538"}, {file = "Pillow-10.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d737a602fbd82afd892ca746392401b634e278cb65d55c4b7a8f48e9ef8d008d"}, {file = "Pillow-10.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:3a82c40d706d9aa9734289740ce26460a11aeec2d9c79b7af87bb35f0073c12f"}, + {file = "Pillow-10.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:bc2ec7c7b5d66b8ec9ce9f720dbb5fa4bace0f545acd34870eff4a369b44bf37"}, {file = "Pillow-10.0.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:d80cf684b541685fccdd84c485b31ce73fc5c9b5d7523bf1394ce134a60c6883"}, {file = "Pillow-10.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76de421f9c326da8f43d690110f0e79fe3ad1e54be811545d7d91898b4c8493e"}, {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81ff539a12457809666fef6624684c008e00ff6bf455b4b89fd00a140eecd640"}, @@ -1613,6 +1774,7 @@ files = [ {file = "Pillow-10.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d50b6aec14bc737742ca96e85d6d0a5f9bfbded018264b3b70ff9d8c33485551"}, {file = "Pillow-10.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:00e65f5e822decd501e374b0650146063fbb30a7264b4d2744bdd7b913e0cab5"}, {file = "Pillow-10.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:f31f9fdbfecb042d046f9d91270a0ba28368a723302786c0009ee9b9f1f60199"}, + {file = "Pillow-10.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:1ce91b6ec08d866b14413d3f0bbdea7e24dfdc8e59f562bb77bc3fe60b6144ca"}, {file = "Pillow-10.0.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:349930d6e9c685c089284b013478d6f76e3a534e36ddfa912cde493f235372f3"}, {file = "Pillow-10.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3a684105f7c32488f7153905a4e3015a3b6c7182e106fe3c37fbb5ef3e6994c3"}, {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4f69b3700201b80bb82c3a97d5e9254084f6dd5fb5b16fc1a7b974260f89f43"}, @@ -1650,6 +1812,7 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa name = "pkgutil-resolve-name" version = "1.3.10" description = "Resolve a name to an object." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1661,6 +1824,7 @@ files = [ name = "platformdirs" version = "3.8.1" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1672,10 +1836,30 @@ files = [ docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"] +[[package]] +name = "pre-commit" +version = "3.3.3" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pre_commit-3.3.3-py2.py3-none-any.whl", hash = "sha256:10badb65d6a38caff29703362271d7dca483d01da88f9d7e05d0b97171c136cb"}, + {file = "pre_commit-3.3.3.tar.gz", hash = "sha256:a2256f489cd913d575c145132ae196fe335da32d91a8294b7afe6622335dd023"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + [[package]] name = "prometheus-client" version = "0.17.1" description = "Python client for the Prometheus monitoring system." +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1690,6 +1874,7 @@ twisted = ["twisted"] name = "prompt-toolkit" version = "3.0.39" description = "Library for building powerful interactive command lines in Python" +category = "dev" optional = false python-versions = ">=3.7.0" files = [ @@ -1704,6 +1889,7 @@ wcwidth = "*" name = "psutil" version = "5.9.5" description = "Cross-platform lib for process and system monitoring in Python." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1730,6 +1916,7 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] name = "ptyprocess" version = "0.7.0" description = "Run a subprocess in a pseudo terminal" +category = "dev" optional = false python-versions = "*" files = [ @@ -1741,6 +1928,7 @@ files = [ name = "pure-eval" version = "0.2.2" description = "Safely evaluate AST nodes without side effects" +category = "dev" optional = false python-versions = "*" files = [ @@ -1755,6 +1943,7 @@ tests = ["pytest"] name = "pycodestyle" version = "2.10.0" description = "Python style guide checker" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1766,6 +1955,7 @@ files = [ name = "pycparser" version = "2.21" description = "C parser in Python" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1777,6 +1967,7 @@ files = [ name = "pyflakes" version = "3.0.1" description = "passive checker of Python programs" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1788,6 +1979,7 @@ files = [ name = "pygments" version = "2.15.1" description = "Pygments is a syntax highlighting package written in Python." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1802,6 +1994,7 @@ plugins = ["importlib-metadata"] name = "pyparsing" version = "3.0.9" description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "main" optional = false python-versions = ">=3.6.8" files = [ @@ -1816,6 +2009,7 @@ diagrams = ["jinja2", "railroad-diagrams"] name = "python-dateutil" version = "2.8.2" description = "Extensions to the standard Python datetime module" +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -1830,6 +2024,7 @@ six = ">=1.5" name = "pytz" version = "2023.3" description = "World timezone definitions, modern and historical" +category = "main" optional = false python-versions = "*" files = [ @@ -1841,6 +2036,7 @@ files = [ name = "pywin32" version = "306" description = "Python for Window Extensions" +category = "main" optional = false python-versions = "*" files = [ @@ -1864,6 +2060,7 @@ files = [ name = "pywinpty" version = "2.0.10" description = "Pseudo terminal support for Windows from Python." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1875,10 +2072,61 @@ files = [ {file = "pywinpty-2.0.10.tar.gz", hash = "sha256:cdbb5694cf8c7242c2ecfaca35c545d31fa5d5814c3d67a4e628f803f680ebea"}, ] +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + [[package]] name = "pyzmq" version = "25.1.0" description = "Python bindings for 0MQ" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1968,6 +2216,7 @@ cffi = {version = "*", markers = "implementation_name == \"pypy\""} name = "qtconsole" version = "5.4.3" description = "Jupyter Qt console" +category = "dev" optional = false python-versions = ">= 3.7" files = [ @@ -1994,6 +2243,7 @@ test = ["flaky", "pytest", "pytest-qt"] name = "qtpy" version = "2.3.1" description = "Provides an abstraction layer on top of the various Qt bindings (PyQt5/6 and PySide2/6)." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2011,6 +2261,7 @@ test = ["pytest (>=6,!=7.0.0,!=7.0.1)", "pytest-cov (>=3.0.0)", "pytest-qt"] name = "referencing" version = "0.29.1" description = "JSON Referencing + Python" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2026,6 +2277,7 @@ rpds-py = ">=0.7.0" name = "rich" version = "13.4.2" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -2045,6 +2297,7 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] name = "rpds-py" version = "0.8.10" description = "Python bindings to Rust's persistent data structures (rpds)" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2147,10 +2400,38 @@ files = [ {file = "rpds_py-0.8.10.tar.gz", hash = "sha256:13e643ce8ad502a0263397362fb887594b49cf84bf518d6038c16f235f2bcea4"}, ] +[[package]] +name = "ruff" +version = "0.0.284" +description = "An extremely fast Python linter, written in Rust." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.0.284-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:8b949084941232e2c27f8d12c78c5a6a010927d712ecff17231ee1a8371c205b"}, + {file = "ruff-0.0.284-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:a3930d66b35e4dc96197422381dff2a4e965e9278b5533e71ae8474ef202fab0"}, + {file = "ruff-0.0.284-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d1f7096038961d8bc3b956ee69d73826843eb5b39a5fa4ee717ed473ed69c95"}, + {file = "ruff-0.0.284-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bcaf85907fc905d838f46490ee15f04031927bbea44c478394b0bfdeadc27362"}, + {file = "ruff-0.0.284-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3660b85a9d84162a055f1add334623ae2d8022a84dcd605d61c30a57b436c32"}, + {file = "ruff-0.0.284-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:0a3218458b140ea794da72b20ea09cbe13c4c1cdb7ac35e797370354628f4c05"}, + {file = "ruff-0.0.284-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2fe880cff13fffd735387efbcad54ba0ff1272bceea07f86852a33ca71276f4"}, + {file = "ruff-0.0.284-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1d098ea74d0ce31478765d1f8b4fbdbba2efc532397b5c5e8e5ea0c13d7e5ae"}, + {file = "ruff-0.0.284-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c79ae3308e308b94635cd57a369d1e6f146d85019da2fbc63f55da183ee29b"}, + {file = "ruff-0.0.284-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f86b2b1e7033c00de45cc176cf26778650fb8804073a0495aca2f674797becbb"}, + {file = "ruff-0.0.284-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e37e086f4d623c05cd45a6fe5006e77a2b37d57773aad96b7802a6b8ecf9c910"}, + {file = "ruff-0.0.284-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d29dfbe314e1131aa53df213fdfea7ee874dd96ea0dd1471093d93b59498384d"}, + {file = "ruff-0.0.284-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:88295fd649d0aa1f1271441df75bf06266a199497afd239fd392abcfd75acd7e"}, + {file = "ruff-0.0.284-py3-none-win32.whl", hash = "sha256:735cd62fccc577032a367c31f6a9de7c1eb4c01fa9a2e60775067f44f3fc3091"}, + {file = "ruff-0.0.284-py3-none-win_amd64.whl", hash = "sha256:f67ed868d79fbcc61ad0fa034fe6eed2e8d438d32abce9c04b7c4c1464b2cf8e"}, + {file = "ruff-0.0.284-py3-none-win_arm64.whl", hash = "sha256:1292cfc764eeec3cde35b3a31eae3f661d86418b5e220f5d5dba1c27a6eccbb6"}, + {file = "ruff-0.0.284.tar.gz", hash = "sha256:ebd3cc55cd499d326aac17a331deaea29bea206e01c08862f9b5c6e93d77a491"}, +] + [[package]] name = "send2trash" version = "1.8.2" description = "Send file to trash natively under Mac OS X, Windows and Linux" +category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" files = [ @@ -2163,10 +2444,28 @@ nativelib = ["pyobjc-framework-Cocoa", "pywin32"] objc = ["pyobjc-framework-Cocoa"] win32 = ["pywin32"] +[[package]] +name = "setuptools" +version = "68.1.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-68.1.0-py3-none-any.whl", hash = "sha256:e13e1b0bc760e9b0127eda042845999b2f913e12437046e663b833aa96d89715"}, + {file = "setuptools-68.1.0.tar.gz", hash = "sha256:d59c97e7b774979a5ccb96388efc9eb65518004537e85d52e81eaee89ab6dd91"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + [[package]] name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -2178,6 +2477,7 @@ files = [ name = "smartypants" version = "2.0.1" description = "Python with the SmartyPants" +category = "main" optional = false python-versions = "*" files = [ @@ -2188,6 +2488,7 @@ files = [ name = "sniffio" version = "1.3.0" description = "Sniff out which async library your code is running under" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2199,6 +2500,7 @@ files = [ name = "soupsieve" version = "2.4.1" description = "A modern CSS selector implementation for Beautiful Soup." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2210,6 +2512,7 @@ files = [ name = "stack-data" version = "0.6.2" description = "Extract data from python stack frames and tracebacks for informative displays" +category = "dev" optional = false python-versions = "*" files = [ @@ -2229,6 +2532,7 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] name = "terminado" version = "0.17.1" description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2249,6 +2553,7 @@ test = ["pre-commit", "pytest (>=7.0)", "pytest-timeout"] name = "testpath" version = "0.6.0" description = "Test utilities for code working with files and commands" +category = "main" optional = false python-versions = ">= 3.5" files = [ @@ -2263,6 +2568,7 @@ test = ["pytest"] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2274,6 +2580,7 @@ files = [ name = "tornado" version = "6.3.2" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +category = "dev" optional = false python-versions = ">= 3.8" files = [ @@ -2294,6 +2601,7 @@ files = [ name = "traitlets" version = "5.9.0" description = "Traitlets Python configuration system" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2309,6 +2617,7 @@ test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"] name = "typing-extensions" version = "4.7.1" description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2320,6 +2629,7 @@ files = [ name = "typogrify" version = "2.0.7" description = "Filters to enhance web typography, including support for Django & Jinja templates" +category = "main" optional = false python-versions = "*" files = [ @@ -2333,6 +2643,7 @@ smartypants = ">=1.8.3" name = "unidecode" version = "1.3.6" description = "ASCII transliterations of Unicode text" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -2340,10 +2651,32 @@ files = [ {file = "Unidecode-1.3.6.tar.gz", hash = "sha256:fed09cf0be8cf415b391642c2a5addfc72194407caee4f98719e40ec2a72b830"}, ] +[[package]] +name = "virtualenv" +version = "20.24.1" +description = "Virtual Python Environment builder" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.24.1-py3-none-any.whl", hash = "sha256:01aacf8decd346cf9a865ae85c0cdc7f64c8caa07ff0d8b1dfc1733d10677442"}, + {file = "virtualenv-20.24.1.tar.gz", hash = "sha256:2ef6a237c31629da6442b0bcaa3999748108c7166318d1f55cc9f8d7294e97bd"}, +] + +[package.dependencies] +distlib = ">=0.3.6,<1" +filelock = ">=3.12,<4" +platformdirs = ">=3.5.1,<4" + +[package.extras] +docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezer (>=0.4.6)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.8)", "time-machine (>=2.9)"] + [[package]] name = "wcwidth" version = "0.2.6" description = "Measures the displayed width of unicode strings in a terminal" +category = "dev" optional = false python-versions = "*" files = [ @@ -2355,6 +2688,7 @@ files = [ name = "webencodings" version = "0.5.1" description = "Character encoding aliases for legacy web content" +category = "main" optional = false python-versions = "*" files = [ @@ -2366,6 +2700,7 @@ files = [ name = "websocket-client" version = "1.6.1" description = "WebSocket client for Python with low level API options" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2382,6 +2717,7 @@ test = ["websockets"] name = "widgetsnbextension" version = "4.0.8" description = "Jupyter interactive widgets for Jupyter Notebook" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2393,6 +2729,7 @@ files = [ name = "zipp" version = "3.16.1" description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2407,4 +2744,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.8.1,<4.0" -content-hash = "8fc79abda8e5e35b46ff88b464f8a0cbec18bc30f35ab9c5714cf35482a7067f" +content-hash = "82f04cad8577914991aa590f4105ffe4fac0c64b067763ef57b3a2e4cbe93e93" diff --git a/pyproject.toml b/pyproject.toml index 48ec21d..7266e61 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,9 @@ ipython = "^8.8.0" jupyter = "^1.0.0" black = "^22.12.0" flake8 = "^6.0.0" +isort = "^5.12.0" +pre-commit = "^3.3.3" +ruff = "^0.0.284" [build-system] build-backend = "poetry.core.masonry.api"