deployment refactoring

- update CI to correctly build docker images
- add requirement.txt
- update Dockerfile to correctly build the app
This commit is contained in:
Nicola Tarocco 2024-07-26 18:04:53 +02:00 committed by lrdossan
parent 66a5944489
commit 41ea92cac2
12 changed files with 263 additions and 243 deletions

3
.gitignore vendored
View file

@ -3,6 +3,7 @@ __pycache__
*.egg-info *.egg-info
*.DS_Store *.DS_Store
*.pyc *.pyc
dist
# Editor stuff # Editor stuff
*.swp *.swp
@ -18,4 +19,4 @@ app-config/openshift/caimira-test
app-config/openshift/caimira-prod app-config/openshift/caimira-prod
# documentation build folder # documentation build folder
caimira/docs/_build caimira/docs/_build

View file

@ -1,7 +1,7 @@
stages: stages:
- test - test
- docker-build - docker-build
- oc-tag - deploy
# Use the acc-py-devtools templates found at # Use the acc-py-devtools templates found at
# https://gitlab.cern.ch/-/ide/project/acc-co/devops/python/acc-py-devtools/blob/master/-/acc_py_devtools/templates/gitlab-ci/python.yml. # https://gitlab.cern.ch/-/ide/project/acc-co/devops/python/acc-py-devtools/blob/master/-/acc_py_devtools/templates/gitlab-ci/python.yml.
@ -18,6 +18,8 @@ variables:
.test-base: .test-base:
image: registry.cern.ch/docker.io/library/python:${PY_VERSION} image: registry.cern.ch/docker.io/library/python:${PY_VERSION}
stage: test stage: test
except:
- live/caimira-test # do not run tests on live/caimira-test branch
.test-run: .test-run:
extends: extends:
@ -62,11 +64,7 @@ test-cern-caimira-py39:
.test_openshift_config: .test_openshift_config:
stage: test stage: test
rules: allow_failure: true
- if: '$OC_TOKEN && $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == $BRANCH'
allow_failure: true # The branch must represent what is deployed. FIXME: change to true because of a diff between ConfigMaps
- if: '$OC_TOKEN && $CI_MERGE_REQUEST_EVENT_TYPE != "detached"'
allow_failure: true # Anything other than the branch may fail without blocking the pipeline.
image: registry.cern.ch/docker.io/mambaorg/micromamba image: registry.cern.ch/docker.io/mambaorg/micromamba
before_script: before_script:
- micromamba create --yes -p $HOME/env python=3.9 ruamel.yaml wget -c conda-forge - micromamba create --yes -p $HOME/env python=3.9 ruamel.yaml wget -c conda-forge
@ -86,6 +84,9 @@ test-cern-caimira-py39:
paths: paths:
- ./app-config/openshift/${CAIMIRA_INSTANCE}/actual - ./app-config/openshift/${CAIMIRA_INSTANCE}/actual
- ./app-config/openshift/${CAIMIRA_INSTANCE}/expected - ./app-config/openshift/${CAIMIRA_INSTANCE}/expected
only:
- master
- live/caimira-test # do not run tests on live/caimira-test branch
check_openshift_config_test: check_openshift_config_test:
extends: .test_openshift_config extends: .test_openshift_config
@ -108,6 +109,7 @@ check_openshift_config_test:
# ################################################################################################### # ###################################################################################################
# Build docker images # Build docker images
# base
.docker-build: .docker-build:
stage: docker-build stage: docker-build
image: image:
@ -116,6 +118,7 @@ check_openshift_config_test:
name: gcr.io/kaniko-project/executor:debug name: gcr.io/kaniko-project/executor:debug
entrypoint: [""] entrypoint: [""]
script: script:
- echo "Building image for ${CI_COMMIT_REF_NAME} branch with tag ${IMAGE_TAG}"
# Prepare Kaniko configuration file # Prepare Kaniko configuration file
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
- echo "Building ${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:latest Docker image..." - echo "Building ${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:latest Docker image..."
@ -124,67 +127,95 @@ check_openshift_config_test:
# Print the full registry path of the pushed image # Print the full registry path of the pushed image
- echo "Image pushed successfully to ${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:${IMAGE_TAG}" - echo "Image pushed successfully to ${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:${IMAGE_TAG}"
.docker-build-live-test: .docker-build-auth-service:
variables:
IMAGE_TAG: caimira-test-latest
extends: .docker-build
before_script:
- echo "Branch is $CI_COMMIT_REF_NAME"
- echo "Building image for live/caimira-test branch with tag ${IMAGE_TAG}"
only:
- live/caimira-test
.docker-build-release:
extends: .docker-build
before_script:
- echo "Tag is $CI_COMMIT_REF_NAME"
# Extract version number without 'v' prefix as IMAGE_TAG
- IMAGE_TAG=$(echo "$CI_COMMIT_REF_NAME" | sed 's/^v//')
- echo "Version is $IMAGE_TAG"
only:
- tags
build-auth-service-image:
extends:
- .docker-build-live-test
- .docker-build-release
variables: variables:
IMAGE_NAME: auth-service IMAGE_NAME: auth-service
DOCKERFILE_DIRECTORY: app-config/auth-service DOCKERFILE_DIRECTORY: app-config/auth-service
DOCKER_CONTEXT_DIRECTORY: app-config/auth-service DOCKER_CONTEXT_DIRECTORY: app-config/auth-service
extends: .docker-build
build-calculator-app-image: .docker-build-calculator-app:
extends:
- .docker-build-live-test
- .docker-build-release
variables: variables:
IMAGE_NAME: calculator-app IMAGE_NAME: calculator-app
DOCKERFILE_DIRECTORY: app-config/calculator-app DOCKERFILE_DIRECTORY: app-config/calculator-app
DOCKER_CONTEXT_DIRECTORY: "" DOCKER_CONTEXT_DIRECTORY: ""
extends: .docker-build
# on push to live/caimira-test
.docker-build-test:
variables:
IMAGE_TAG: caimira-test-latest
docker-build-auth-service-test:
extends:
- .docker-build-test
- .docker-build-auth-service
only:
- live/caimira-test
docker-build-calculator-app-test:
extends:
- .docker-build-test
- .docker-build-calculator-app
only:
- live/caimira-test
# on release
.docker-build-release:
before_script:
# Extract version number without 'v' prefix as IMAGE_TAG
- IMAGE_TAG=$(echo "$CI_COMMIT_REF_NAME" | sed 's/^v//')
- echo "Version is $IMAGE_TAG"
docker-build-auth-service-release:
extends:
- .docker-build-release
- .docker-build-auth-service
only:
- tag
docker-build-calculator-app-release:
extends:
- .docker-build-release
- .docker-build-calculator-app
only:
- tag
# ################################################################################################### # ###################################################################################################
# Link build Docker images OpenShift <-> GitLab registry # Deploy to OpenShift
.deploy:
.link_docker_images_with_gitlab_registry: stage: deploy
stage: oc-tag image: gitlab-registry.cern.ch/paas-tools/openshift-client
image: gitlab-registry.cern.ch/paas-tools/openshift-client:latest
variables: variables:
OC_PROJECT: "caimira-test"
OC_TOKEN: ${OPENSHIFT_CAIMIRA_TEST_DEPLOY_TOKEN}
IMAGE_TAG: caimira-test-latest IMAGE_TAG: caimira-test-latest
OPENSHIFT_SERVER: https://api.paas.okd.cern.ch
OPENSHIFT_PROJECT: caimira-test
script: script:
- oc tag --source=docker ${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:${IMAGE_TAG} ${IMAGE_NAME}:latest --token ${OC_TOKEN} --server=https://api.paas.okd.cern.ch -n ${OC_PROJECT} - echo "Deploying ${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:${IMAGE_TAG} to OpenShift"
- oc login $OPENSHIFT_SERVER --token=$OPENSHIFT_CAIMIRA_TEST_DEPLOY_TOKEN
- oc project $OPENSHIFT_PROJECT
- oc set image dc/$OPENSHIFT_DEPLOYMENT $OPENSHIFT_CONTAINER_NAME=${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:${IMAGE_TAG}
- oc rollout status dc/$OPENSHIFT_DEPLOYMENT
only: only:
- live/caimira-test # for prod, we want to manually deploy the tag that we need - live/caimira-test
link_auth-service_with_gitlab_registry: deploy-auth-service-test:
extends: extends: .deploy
- .link_docker_images_with_gitlab_registry
variables: variables:
IMAGE_NAME: auth-service IMAGE_NAME: auth-service
OPENSHIFT_DEPLOYMENT: auth-service
OPENSHIFT_CONTAINER_NAME: auth-service
link_calculator-app_with_gitlab_registry: deploy-calculator-app-test:
extends: extends: .deploy
- .link_docker_images_with_gitlab_registry
variables: variables:
IMAGE_NAME: calculator-app IMAGE_NAME: calculator-app
OPENSHIFT_DEPLOYMENT: calculator-app
OPENSHIFT_CONTAINER_NAME: calculator-app
deploy-calculator-open-app-test:
extends: .deploy
variables:
IMAGE_NAME: calculator-app
OPENSHIFT_DEPLOYMENT: calculator-open-app
OPENSHIFT_CONTAINER_NAME: calculator-open-app

View file

@ -103,25 +103,25 @@ pip install -e . # At the root of the repository
### Running the Calculator app in development mode ### Running the Calculator app in development mode
``` ```
python -m ui.apps.calculator python -m cern_caimira.apps.calculator
``` ```
To run with a specific template theme created: To run with a specific template theme created:
``` ```
python -m ui.apps.calculator --theme=ui/apps/templates/{theme} python -m cern_caimira.apps.calculator --theme=ui/apps/templates/{theme}
``` ```
To run the entire app in a different `APPLICATION_ROOT` path: To run the entire app in a different `APPLICATION_ROOT` path:
``` ```
python -m ui.apps.calculator --app_root=/myroot python -m cern_caimira.apps.calculator --app_root=/myroot
``` ```
To run the calculator on a different URL path: To run the calculator on a different URL path:
``` ```
python -m ui.apps.calculator --prefix=/mycalc python -m cern_caimira.apps.calculator --prefix=/mycalc
``` ```
Each of these commands will start a local version of CAiMIRA, which can be visited at http://localhost:8080/. Each of these commands will start a local version of CAiMIRA, which can be visited at http://localhost:8080/.

View file

@ -1,22 +1,33 @@
FROM registry.cern.ch/docker.io/condaforge/mambaforge as conda FROM registry.cern.ch/docker.io/condaforge/mambaforge AS conda
ARG PYTHON_VERSION=3.12
RUN mamba create --yes -p /opt/app python=${PYTHON_VERSION}
RUN mamba create --yes -p /opt/app python=3.9
COPY . /opt/app-source COPY . /opt/app-source
RUN cd /opt/app-source && conda run -p /opt/app python -m pip install -r ./requirements.txt .[app] WORKDIR /opt/app-source
# install Python deps
RUN cd cern_caimira \
&& conda run -p /opt/app python -m pip install -r requirements.txt
RUN cd caimira \
&& conda run -p /opt/app python -m pip install .
RUN cd cern_caimira \
&& conda run -p /opt/app python -m pip install .
COPY app-config/calculator-app/app.sh /opt/app/bin/calculator-app.sh COPY app-config/calculator-app/app.sh /opt/app/bin/calculator-app.sh
RUN cd /opt/app \ RUN cd /opt/app \
&& find -name '*.a' -delete \ && find -name '*.a' -delete \
&& rm -rf /opt/app/conda-meta \ && rm -rf /opt/app/conda-meta \
&& rm -rf /opt/app/include \ && rm -rf /opt/app/include \
&& find -name '__pycache__' -type d -exec rm -rf '{}' '+' \ && find -name '__pycache__' -type d -exec rm -rf '{}' '+' \
&& rm -rf /opt/app/lib/python*/site-packages/pip /opt/app/lib/python*/idlelib /opt/app/lib/python*/ensurepip \ && rm -rf /opt/app/lib/python*/site-packages/pip /opt/app/lib/python*/idlelib /opt/app/lib/python*/ensurepip \
/opt/app/bin/x86_64-conda-linux-gnu-ld \ /opt/app/bin/x86_64-conda-linux-gnu-ld \
/opt/app/bin/sqlite3 \ /opt/app/bin/sqlite3 \
/opt/app/bin/openssl \ /opt/app/bin/openssl \
/opt/app/share/terminfo \ /opt/app/share/terminfo \
&& find /opt/app/lib/ -name 'tests' -type d -exec rm -rf '{}' '+' \ && find /opt/app/lib/ -name 'tests' -type d -exec rm -rf '{}' '+' \
&& find /opt/app/lib -name '*.pyx' -delete \ && find /opt/app/lib -name '*.pyx' -delete \
; ;
FROM registry.cern.ch/docker.io/library/debian FROM registry.cern.ch/docker.io/library/debian
@ -25,12 +36,10 @@ ENV PATH=/opt/app/bin/:$PATH
# Make a convenient location to the installed CAiMIRA package (i.e. a directory called caimira in the CWD). # Make a convenient location to the installed CAiMIRA package (i.e. a directory called caimira in the CWD).
# It is important that this directory is also writable by a non-root user. # It is important that this directory is also writable by a non-root user.
RUN mkdir -p /scratch \ RUN mkdir -p /scratch \
&& chmod a+wx /scratch && chmod a+wx /scratch
# Set the HOME directory to something that anybody can write to (to support non root users, such as on openshift). # Set the HOME directory to something that anybody can write to (to support non root users, such as on openshift).
ENV HOME=/scratch ENV HOME=/scratch
WORKDIR /scratch WORKDIR /scratch
RUN CAIMIRA_INIT_FILE=$(/opt/app/bin/python -c "import caimira; print(caimira.__file__)") \ RUN CERN_CAIMIRA_INIT_FILE=$(python -c "import cern_caimira; print(cern_caimira.__file__)") \
&& ln -s $(dirname ${CAIMIRA_INIT_FILE}) /scratch/caimira && ln -s $(dirname ${CERN_CAIMIRA_INIT_FILE}) /scratch/cern_caimira
CMD [ \ CMD [ "calculator-app.sh" ]
"calculator-app.sh" \
]

View file

@ -16,7 +16,7 @@ if [[ "$APP_NAME" == "calculator-app" ]]; then
if [ ! -z "$CAIMIRA_THEME" ]; then if [ ! -z "$CAIMIRA_THEME" ]; then
args+=("--theme=${CAIMIRA_THEME}") args+=("--theme=${CAIMIRA_THEME}")
fi fi
export "ARVE_API_KEY"="$ARVE_API_KEY" export "ARVE_API_KEY"="$ARVE_API_KEY"
export "ARVE_CLIENT_ID"="$ARVE_CLIENT_ID" export "ARVE_CLIENT_ID"="$ARVE_CLIENT_ID"
export "ARVE_CLIENT_SECRET"="$ARVE_CLIENT_SECRET" export "ARVE_CLIENT_SECRET"="$ARVE_CLIENT_SECRET"
@ -26,8 +26,8 @@ if [[ "$APP_NAME" == "calculator-app" ]]; then
export "DATA_SERVICE_ENABLED"="${DATA_SERVICE_ENABLED:=0}" export "DATA_SERVICE_ENABLED"="${DATA_SERVICE_ENABLED:=0}"
export "CAIMIRA_PROFILER_ENABLED"="${CAIMIRA_PROFILER_ENABLED:=0}" export "CAIMIRA_PROFILER_ENABLED"="${CAIMIRA_PROFILER_ENABLED:=0}"
echo "Starting the caimira webservice with: python -m ui.apps.calculator ${args[@]}" echo "Starting the caimira webservice with: python -m cern_caimira.apps.calculator ${args[@]}"
python -m ui.apps.calculator "${args[@]}" python -m cern_caimira.apps.calculator "${args[@]}"
else else
echo "No APP_NAME specified" echo "No APP_NAME specified"

View file

@ -1,103 +1,82 @@
worker_processes auto; tcp_nopush on;
error_log /var/log/nginx/error.log; tcp_nodelay on;
pid /run/nginx.pid; types_hash_max_size 2048;
include /usr/share/nginx/modules/*.conf; server {
listen 8080 default_server;
listen [::]:8080 default_server;
server_name _;
root /opt/app-root/src;
events { # Load configuration files for the default server block.
worker_connections 1024; include /opt/app-root/etc/nginx.default.d/*.conf;
}
http { large_client_header_buffers 4 16k;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
sendfile on; error_page 404 /404.html;
tcp_nopush on; location = /40x.html {
tcp_nodelay on; }
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types; error_page 500 502 503 504 /50x.html;
default_type application/octet-stream; location = /50x.html {
}
server { proxy_http_version 1.1;
listen 8080 default_server; proxy_set_header Upgrade $http_upgrade;
listen [::]:8080 default_server; proxy_set_header Connection "upgrade";
server_name _; proxy_read_timeout 86400;
root /opt/app-root/src;
# Load configuration files for the default server block. proxy_set_header Host $http_host;
include /opt/app-root/etc/nginx.default.d/*.conf; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
large_client_header_buffers 4 16k; location /auth {
proxy_pass_request_body off;
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header Content-Length "";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header If-None-Match "";
proxy_pass http://auth-service:8080;
}
location /auth { location @error401 {
proxy_pass_request_body off; # Store the request_uri (complete with args) to be redirected to
# when we hit /auth/complete.
add_header Set-Cookie "POST_AUTH_REDIRECT=$request_uri;";
return 302 /auth/login;
}
proxy_set_header Host $http_host; location @proxy_404_error_handler {
proxy_set_header Content-Length ""; # Pass the request on to the webservice. Most likely the URI won't
proxy_set_header If-None-Match ""; # exist so we get a 404 from that service instead (good as the 404
proxy_pass http://auth-service:8080; # pages are consistent).
} proxy_pass http://calculator-app:8080/$request_uri;
}
location @error401 { # Redirect URLs to the new scheme.
# Store the request_uri (complete with args) to be redirected to absolute_redirect off;
# when we hit /auth/complete.
add_header Set-Cookie "POST_AUTH_REDIRECT=$request_uri;";
return 302 /auth/login;
}
location @proxy_404_error_handler { location / {
# Pass the request on to the webservice. Most likely the URI won't # By default we have no authentication.
# exist so we get a 404 from that service instead (good as the 404 proxy_pass http://calculator-app:8080;
# pages are consistent). }
proxy_pass http://calculator-app:8080/$request_uri;
}
# Redirect URLs to the new scheme. location /calculator {
absolute_redirect off; return 302 /calculator-cern$is_args$args;
}
location / { location /calculator-cern {
# By default we have no authentication. # CERN calculator is authenticated.
proxy_pass http://calculator-app:8080; auth_request /auth/probe;
} error_page 401 = @error401;
location /calculator { # calculator-app is the name of the tornado server (for the calculator)
return 302 /calculator-cern$is_args$args; # in each of docker-compose, caimira-test.web.cern.ch and caimira.web.cern.ch.
} proxy_pass http://calculator-app:8080/calculator-cern;
}
location /calculator-cern { location /calculator-open {
# CERN calculator is authenticated. # Public open calculator
auth_request /auth/probe; proxy_pass http://calculator-open-app:8080/calculator-open;
error_page 401 = @error401;
# calculator-app is the name of the tornado server (for the calculator)
# in each of docker-compose, caimira-test.web.cern.ch and caimira.web.cern.ch.
proxy_pass http://calculator-app:8080/calculator-cern;
}
location /calculator-open {
# Public open calculator
proxy_pass http://calculator-open-app:8080/calculator-open;
}
} }
} }

View file

@ -56,9 +56,8 @@ doc = [
[project.urls] [project.urls]
Homepage = "https://github.com/cern/caimira" Homepage = "https://github.com/cern/caimira"
[tool.setuptools] [tool.setuptools.package-data]
packages = ["caimira"] caimira = ["**/*"]
package-dir = {"" = "src"}
[tool.pytest.ini_options] [tool.pytest.ini_options]
addopts = "--mypy" addopts = "--mypy"

View file

@ -56,9 +56,8 @@ doc = [
[project.urls] [project.urls]
Homepage = "https://cern.ch/caimira" Homepage = "https://cern.ch/caimira"
[tool.setuptools] [tool.setuptools.package-data]
packages = ["cern_caimira"] cern_caimira = ["**/*"]
package-dir = {"" = "src"}
[tool.pytest.ini_options] [tool.pytest.ini_options]
addopts = "--mypy" addopts = "--mypy"

View file

@ -1,9 +1,8 @@
# Created by installing the caimira[core] extra and running: # Created by:
# echo '.[app]' > requirements.txt # 1. installing the caimira and cern_caimira
# pip list --format freeze | grep -vi caimira | grep -v pip | grep -v setuptools >> requirements.txt # 2. running `pip freeze > requirements.txt`
# 3. removing the local caimira and cern_caimira apps from the list
.[app] anyio==4.4.0
anyio==4.2.0
appnope==0.1.4 appnope==0.1.4
argon2-cffi==23.1.0 argon2-cffi==23.1.0
argon2-cffi-bindings==21.2.0 argon2-cffi-bindings==21.2.0
@ -11,114 +10,117 @@ arrow==1.3.0
asttokens==2.4.1 asttokens==2.4.1
async-lru==2.0.4 async-lru==2.0.4
attrs==23.2.0 attrs==23.2.0
Babel==2.14.0 Babel==2.15.0
beautifulsoup4==4.12.3 beautifulsoup4==4.12.3
bleach==6.1.0 bleach==6.1.0
certifi==2024.2.2 certifi==2024.7.4
cffi==1.16.0 cffi==1.16.0
charset-normalizer==3.3.2 charset-normalizer==3.3.2
cloudpickle==3.0.0 cloudpickle==3.0.0
comm==0.2.1 comm==0.2.2
contourpy==1.2.0 contourpy==1.2.1
cycler==0.12.1 cycler==0.12.1
debugpy==1.8.1 debugpy==1.8.2
decorator==5.1.1 decorator==5.1.1
defusedxml==0.7.1 defusedxml==0.7.1
exceptiongroup==1.2.0
executing==2.0.1 executing==2.0.1
fastjsonschema==2.19.1 fastjsonschema==2.20.0
fonttools==4.49.0 fonttools==4.53.1
fqdn==1.5.1 fqdn==1.5.1
h11==0.14.0 h11==0.14.0
h3==3.7.6 h3==3.7.7
httpcore==1.0.3 httpcore==1.0.5
httpx==0.26.0 httpx==0.27.0
idna==3.6 idna==3.7
importlib-metadata==7.0.1 ipykernel==6.29.5
importlib-resources==6.1.1 ipympl==0.9.4
ipykernel==6.29.2 ipython==8.26.0
ipympl==0.9.3
ipython==8.18.1
ipython-genutils==0.2.0 ipython-genutils==0.2.0
ipywidgets==7.8.1 ipywidgets==7.8.2
isoduration==20.11.0 isoduration==20.11.0
jedi==0.19.1 jedi==0.19.1
Jinja2==3.1.3 Jinja2==3.1.4
joblib==1.3.2 joblib==1.4.2
json5==0.9.14 json5==0.9.25
jsonpointer==2.4 jsonpointer==3.0.0
jsonschema==4.21.1 jsonschema==4.23.0
jsonschema-specifications==2023.12.1 jsonschema-specifications==2023.12.1
jupyter-events==0.10.0
jupyter-lsp==2.2.5
jupyter_client==8.6.2
jupyter_core==5.7.2
jupyter_server==2.14.2
jupyter_server_terminals==0.5.3
jupyterlab==4.2.4
jupyterlab_pygments==0.3.0
jupyterlab_server==2.27.3
jupyterlab_widgets==1.1.8
kiwisolver==1.4.5 kiwisolver==1.4.5
loky==3.4.1 loky==3.4.1
MarkupSafe==2.1.5 MarkupSafe==2.1.5
matplotlib==3.8.3 matplotlib==3.9.1
matplotlib-inline==0.1.6 matplotlib-inline==0.1.7
memoization==0.4.0 memoization==0.4.0
mistune==3.0.2 mistune==3.0.2
nbclient==0.7.4 nbclient==0.10.0
nbconvert==7.16.0 nbconvert==7.16.4
nbformat==5.9.2 nbformat==5.10.4
nest-asyncio==1.6.0 nest-asyncio==1.6.0
notebook==7.1.0 notebook==7.2.1
notebook_shim==0.2.4 notebook_shim==0.2.4
numpy==1.26.4 numpy==2.0.1
overrides==7.7.0 overrides==7.7.0
packaging==23.2 packaging==24.1
pandas==2.2.0 pandas==2.2.2
pandocfilters==1.5.1 pandocfilters==1.5.1
parso==0.8.3 parso==0.8.4
pexpect==4.9.0 pexpect==4.9.0
pillow==10.2.0 pillow==10.4.0
platformdirs==4.2.0 platformdirs==4.2.2
prometheus_client==0.20.0 prometheus_client==0.20.0
prompt-toolkit==3.0.43 prompt_toolkit==3.0.47
psutil==5.9.8 psutil==6.0.0
ptyprocess==0.7.0 ptyprocess==0.7.0
pure-eval==0.2.2 pure_eval==0.2.3
py==1.11.0 py==1.11.0
pycparser==2.21 pycparser==2.22
Pygments==2.17.2 Pygments==2.18.0
pyinstrument==4.6.2 pyinstrument==4.6.2
PyJWT==2.8.0 pyparsing==3.1.2
pyparsing==3.1.1 python-dateutil==2.9.0.post0
python-dateutil==2.8.2
python-json-logger==2.0.7 python-json-logger==2.0.7
pytz==2024.1 pytz==2024.1
PyYAML==6.0.1 PyYAML==6.0.1
pyzmq==25.1.2 pyzmq==26.0.3
referencing==0.33.0 referencing==0.35.1
requests==2.31.0 requests==2.32.3
retry==0.9.2 retry==0.9.2
rfc3339-validator==0.1.4 rfc3339-validator==0.1.4
rfc3986-validator==0.1.1 rfc3986-validator==0.1.1
rpds-py==0.18.0 rpds-py==0.19.1
ruptures==1.1.9 ruptures==1.1.9
scikit-learn==1.4.1.post1 scikit-learn==1.5.1
scipy==1.12.0 scipy==1.14.0
Send2Trash==1.8.2 Send2Trash==1.8.3
setuptools==71.1.0
six==1.16.0 six==1.16.0
sniffio==1.3.0 sniffio==1.3.1
soupsieve==2.5 soupsieve==2.5
stack-data==0.6.3 stack-data==0.6.3
terminado==0.18.0 tabulate==0.9.0
threadpoolctl==3.3.0 terminado==0.18.1
timezonefinder==6.4.1 threadpoolctl==3.5.0
tinycss2==1.2.1 timezonefinder==6.5.2
tomli==2.0.1 tinycss2==1.3.0
tornado==6.4 tornado==6.4.1
traitlets==5.14.1 traitlets==5.14.3
types-python-dateutil==2.8.19.20240106 types-python-dateutil==2.9.0.20240316
types-retry==0.9.9.4 types-retry==0.9.9.4
typing_extensions==4.9.0
tzdata==2024.1 tzdata==2024.1
uri-template==1.3.0 uri-template==1.3.0
urllib3==2.2.0 urllib3==2.2.2
wcwidth==0.2.13 wcwidth==0.2.13
webcolors==1.13 webcolors==24.6.0
webencodings==0.5.1 webencodings==0.5.1
websocket-client==1.7.0 websocket-client==1.8.0
websockets==12.0 widgetsnbextension==3.6.7
wheel==0.41.3
widgetsnbextension==3.6.6
zipp==3.17.0

View file

@ -0,0 +1,3 @@
import importlib.metadata
__version__ = importlib.metadata.version(__package__ or __name__)

View file

@ -1,3 +0,0 @@
import importlib.metadata
__version__ = importlib.metadata.version(__package__ or __name__)